typVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "typ"}}
idataVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "idata"}}
okVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "ok"}}
- deltaVar = Node{Op: ONAME, Class: Pxxx, Sym: &Sym{Name: "delta"}}
)
// startBlock sets the current block we're generating code in to b.
}
// Generate the following code assuming that indexes are in bounds.
- // The conditional is to make sure that we don't generate a slice
+ // The masking is to make sure that we don't generate a slice
// that points to the next object in memory.
- // rlen = j-i
- // rcap = k-i
- // delta = i*elemsize
- // if rcap == 0 {
- // delta = 0
- // }
- // rptr = p+delta
+ // rlen = j - i
+ // rcap = k - i
+ // delta = i * elemsize
+ // rptr = p + delta&mask(rcap)
// result = (SliceMake rptr rlen rcap)
+ // where mask(x) is 0 if x==0 and -1 if x>0.
subOp := s.ssaOp(OSUB, Types[TINT])
- eqOp := s.ssaOp(OEQ, Types[TINT])
mulOp := s.ssaOp(OMUL, Types[TINT])
+ andOp := s.ssaOp(OAND, Types[TINT])
rlen := s.newValue2(subOp, Types[TINT], j, i)
var rcap *ssa.Value
switch {
rcap = s.newValue2(subOp, Types[TINT], k, i)
}
- // delta = # of elements to offset pointer by.
- s.vars[&deltaVar] = i
-
- // Generate code to set delta=0 if the resulting capacity is zero.
- if !((i.Op == ssa.OpConst64 && i.AuxInt == 0) ||
- (i.Op == ssa.OpConst32 && int32(i.AuxInt) == 0)) {
- cmp := s.newValue2(eqOp, Types[TBOOL], rcap, zero)
-
- b := s.endBlock()
- b.Kind = ssa.BlockIf
- b.Likely = ssa.BranchUnlikely
- b.SetControl(cmp)
-
- // Generate block which zeros the delta variable.
- nz := s.f.NewBlock(ssa.BlockPlain)
- b.AddEdgeTo(nz)
- s.startBlock(nz)
- s.vars[&deltaVar] = zero
- s.endBlock()
-
- // All done.
- merge := s.f.NewBlock(ssa.BlockPlain)
- b.AddEdgeTo(merge)
- nz.AddEdgeTo(merge)
- s.startBlock(merge)
-
- // TODO: use conditional moves somehow?
+ var rptr *ssa.Value
+ if (i.Op == ssa.OpConst64 || i.Op == ssa.OpConst32) && i.AuxInt == 0 {
+ // No pointer arithmetic necessary.
+ rptr = ptr
+ } else {
+ // delta = # of bytes to offset pointer by.
+ delta := s.newValue2(mulOp, Types[TINT], i, s.constInt(Types[TINT], elemtype.Width))
+ // If we're slicing to the point where the capacity is zero,
+ // zero out the delta.
+ mask := s.newValue1(ssa.OpSlicemask, Types[TINT], rcap)
+ delta = s.newValue2(andOp, Types[TINT], delta, mask)
+ // Compute rptr = ptr + delta
+ rptr = s.newValue2(ssa.OpAddPtr, ptrtype, ptr, delta)
}
- // Compute rptr = ptr + delta * elemsize
- rptr := s.newValue2(ssa.OpAddPtr, ptrtype, ptr, s.newValue2(mulOp, Types[TINT], s.variable(&deltaVar, Types[TINT]), s.constInt(Types[TINT], elemtype.Width)))
- delete(s.vars, &deltaVar)
return rptr, rlen, rcap
}
(ZeroExt16to32 x) -> (MOVWLZX x)
(Signmask x) -> (SARLconst x [31])
-(Zeromask <t> x) -> (XORLconst [-1] (SBBLcarrymask <t> (CMPL x (MOVLconst [1]))))
+(Zeromask <t> x) -> (XORLconst [-1] (SBBLcarrymask <t> (CMPLconst x [1])))
+(Slicemask <t> x) -> (XORLconst [-1] (SARLconst <t> (SUBLconst <t> x [1]) [31]))
// Lowering truncation
// Because we ignore high parts of registers, truncates are just copies.
(ZeroExt16to64 x) -> (MOVWQZX x)
(ZeroExt32to64 x) -> (MOVLQZX x)
+(Slicemask <t> x) -> (XORQconst [-1] (SARQconst <t> (SUBQconst <t> x [1]) [63]))
+
// Lowering truncation
// Because we ignore high parts of registers, truncates are just copies.
(Trunc16to8 x) -> x
(Signmask x) -> (SRAconst x [31])
(Zeromask x) -> (SRAconst (RSBshiftRL <config.fe.TypeInt32()> x x [1]) [31]) // sign bit of uint32(x)>>1 - x
+(Slicemask <t> x) -> (MVN (SRAconst <t> (SUBconst <t> x [1]) [31]))
// float <-> int conversion
(Cvt32to32F x) -> (MOVWF x)
(ConstNil) -> (MOVDconst [0])
(ConstBool [b]) -> (MOVDconst [b])
+(Slicemask <t> x) -> (MVN (SRAconst <t> (SUBconst <t> x [1]) [63]))
+
// truncations
// Because we ignore high parts of registers, truncates are just copies.
(Trunc16to8 x) -> x
(OrB x y) -> (OR x y)
(EqB x y) -> (XOR (MOVVconst [1]) (XOR <config.fe.TypeBool()> x y))
(NeqB x y) -> (XOR x y)
-(Not x) -> (XOR (MOVVconst [1]) x)
+(Not x) -> (XORconst [1] x)
// constants
(Const64 [val]) -> (MOVVconst [val])
(ConstNil) -> (MOVVconst [0])
(ConstBool [b]) -> (MOVVconst [b])
+(Slicemask <t> x) -> (NORconst [0] (SRAVconst <t> (SUBVconst <t> x [1]) [63]))
+
// truncations
// Because we ignore high parts of registers, truncates are just copies.
(Trunc16to8 x) -> x
(Trunc64to16 x) -> (MOVHreg x)
(Trunc64to32 x) -> (MOVWreg x)
+(Slicemask <t> x) -> (XORconst [-1] (SRADconst <t> (ADDconst <t> x [-1]) [63]))
+
// Note that MOV??reg returns a 64-bit int, x is not necessarily that wide
// This may interact with other patterns in the future. (Compare with arm64)
(MOVBZreg x:(MOVBZload _ _)) -> x
(ZeroExt16to64 x) -> (MOVHZreg x)
(ZeroExt32to64 x) -> (MOVWZreg x)
+(Slicemask <t> x) -> (XOR (MOVDconst [-1]) (SRADconst <t> (SUBconst <t> x [1]) [63]))
+
// Lowering truncation
// Because we ignore high parts of registers, truncates are just copies.
(Trunc16to8 x) -> x
(Trunc32to16 (And32 (Const32 [y]) x)) && y&0xFFFF == 0xFFFF -> (Trunc32to16 x)
(Trunc16to8 (And16 (Const16 [y]) x)) && y&0xFF == 0xFF -> (Trunc16to8 x)
+(Slicemask (Const32 [x])) && x > 0 -> (Const32 [-1])
+(Slicemask (Const32 [0])) -> (Const32 [0])
+(Slicemask (Const64 [x])) && x > 0 -> (Const64 [-1])
+(Slicemask (Const64 [0])) -> (Const64 [0])
+
// Rewrite AND of consts as shifts if possible, slightly faster for 64 bit operands
// leading zeros can be shifted left, then right
(And64 <t> (Const64 [y]) x) && nlz(y) + nto(y) == 64 && nto(y) >= 32
{name: "Signmask", argLength: 1, typ: "Int32"}, // 0 if arg0 >= 0, -1 if arg0 < 0
{name: "Zeromask", argLength: 1, typ: "UInt32"}, // 0 if arg0 == 0, 0xffffffff if arg0 != 0
+ {name: "Slicemask", argLength: 1}, // 0 if arg0 == 0, -1 if arg0 > 0, undef if arg0<0. Type is native int size.
{name: "Cvt32Uto32F", argLength: 1}, // uint32 -> float32, only used on 32-bit arch
{name: "Cvt32Uto64F", argLength: 1}, // uint32 -> float64, only used on 32-bit arch
OpSub32withcarry
OpSignmask
OpZeromask
+ OpSlicemask
OpCvt32Uto32F
OpCvt32Uto64F
OpCvt32Fto32U
argLen: 1,
generic: true,
},
+ {
+ name: "Slicemask",
+ argLen: 1,
+ generic: true,
+ },
{
name: "Cvt32Uto32F",
argLen: 1,
// simplifyBlock simplifies block known the restrictions in ft.
// Returns which branch must always be taken.
func simplifyBlock(ft *factsTable, b *Block) branch {
+ for _, v := range b.Values {
+ if v.Op != OpSlicemask {
+ continue
+ }
+ add := v.Args[0]
+ if add.Op != OpAdd64 && add.Op != OpAdd32 {
+ continue
+ }
+ // Note that the arg of slicemask was originally a sub, but
+ // was rewritten to an add by generic.rules (if the thing
+ // being subtracted was a constant).
+ x := add.Args[0]
+ y := add.Args[1]
+ if x.Op == OpConst64 || x.Op == OpConst32 {
+ x, y = y, x
+ }
+ if y.Op != OpConst64 && y.Op != OpConst32 {
+ continue
+ }
+ // slicemask(x + y)
+ // if x is larger than -y (y is negative), then slicemask is -1.
+ lim, ok := ft.limits[x.ID]
+ if !ok {
+ continue
+ }
+ if lim.umin > uint64(-y.AuxInt) {
+ if v.Args[0].Op == OpAdd64 {
+ v.reset(OpConst64)
+ } else {
+ v.reset(OpConst32)
+ }
+ if b.Func.pass.debug > 0 {
+ b.Func.Config.Warnl(v.Line, "Proved slicemask not needed")
+ }
+ v.AuxInt = -1
+ }
+ }
+
if b.Kind != BlockIf {
return unknown
}
return rewriteValue386_OpSignExt8to32(v, config)
case OpSignmask:
return rewriteValue386_OpSignmask(v, config)
+ case OpSlicemask:
+ return rewriteValue386_OpSlicemask(v, config)
case OpSqrt:
return rewriteValue386_OpSqrt(v, config)
case OpStaticCall:
return true
}
}
+func rewriteValue386_OpSlicemask(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (Slicemask <t> x)
+ // cond:
+ // result: (XORLconst [-1] (SARLconst <t> (SUBLconst <t> x [1]) [31]))
+ for {
+ t := v.Type
+ x := v.Args[0]
+ v.reset(Op386XORLconst)
+ v.AuxInt = -1
+ v0 := b.NewValue0(v.Line, Op386SARLconst, t)
+ v0.AuxInt = 31
+ v1 := b.NewValue0(v.Line, Op386SUBLconst, t)
+ v1.AuxInt = 1
+ v1.AddArg(x)
+ v0.AddArg(v1)
+ v.AddArg(v0)
+ return true
+ }
+}
func rewriteValue386_OpSqrt(v *Value, config *Config) bool {
b := v.Block
_ = b
_ = b
// match: (Zeromask <t> x)
// cond:
- // result: (XORLconst [-1] (SBBLcarrymask <t> (CMPL x (MOVLconst [1]))))
+ // result: (XORLconst [-1] (SBBLcarrymask <t> (CMPLconst x [1])))
for {
t := v.Type
x := v.Args[0]
v.reset(Op386XORLconst)
v.AuxInt = -1
v0 := b.NewValue0(v.Line, Op386SBBLcarrymask, t)
- v1 := b.NewValue0(v.Line, Op386CMPL, TypeFlags)
+ v1 := b.NewValue0(v.Line, Op386CMPLconst, TypeFlags)
+ v1.AuxInt = 1
v1.AddArg(x)
- v2 := b.NewValue0(v.Line, Op386MOVLconst, config.fe.TypeUInt32())
- v2.AuxInt = 1
- v1.AddArg(v2)
v0.AddArg(v1)
v.AddArg(v0)
return true
return rewriteValueAMD64_OpSignExt8to32(v, config)
case OpSignExt8to64:
return rewriteValueAMD64_OpSignExt8to64(v, config)
+ case OpSlicemask:
+ return rewriteValueAMD64_OpSlicemask(v, config)
case OpSqrt:
return rewriteValueAMD64_OpSqrt(v, config)
case OpStaticCall:
return true
}
}
+func rewriteValueAMD64_OpSlicemask(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (Slicemask <t> x)
+ // cond:
+ // result: (XORQconst [-1] (SARQconst <t> (SUBQconst <t> x [1]) [63]))
+ for {
+ t := v.Type
+ x := v.Args[0]
+ v.reset(OpAMD64XORQconst)
+ v.AuxInt = -1
+ v0 := b.NewValue0(v.Line, OpAMD64SARQconst, t)
+ v0.AuxInt = 63
+ v1 := b.NewValue0(v.Line, OpAMD64SUBQconst, t)
+ v1.AuxInt = 1
+ v1.AddArg(x)
+ v0.AddArg(v1)
+ v.AddArg(v0)
+ return true
+ }
+}
func rewriteValueAMD64_OpSqrt(v *Value, config *Config) bool {
b := v.Block
_ = b
return rewriteValueARM_OpSignExt8to32(v, config)
case OpSignmask:
return rewriteValueARM_OpSignmask(v, config)
+ case OpSlicemask:
+ return rewriteValueARM_OpSlicemask(v, config)
case OpSqrt:
return rewriteValueARM_OpSqrt(v, config)
case OpStaticCall:
return true
}
}
+func rewriteValueARM_OpSlicemask(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (Slicemask <t> x)
+ // cond:
+ // result: (MVN (SRAconst <t> (SUBconst <t> x [1]) [31]))
+ for {
+ t := v.Type
+ x := v.Args[0]
+ v.reset(OpARMMVN)
+ v0 := b.NewValue0(v.Line, OpARMSRAconst, t)
+ v0.AuxInt = 31
+ v1 := b.NewValue0(v.Line, OpARMSUBconst, t)
+ v1.AuxInt = 1
+ v1.AddArg(x)
+ v0.AddArg(v1)
+ v.AddArg(v0)
+ return true
+ }
+}
func rewriteValueARM_OpSqrt(v *Value, config *Config) bool {
b := v.Block
_ = b
return rewriteValueARM64_OpSignExt8to32(v, config)
case OpSignExt8to64:
return rewriteValueARM64_OpSignExt8to64(v, config)
+ case OpSlicemask:
+ return rewriteValueARM64_OpSlicemask(v, config)
case OpSqrt:
return rewriteValueARM64_OpSqrt(v, config)
case OpStaticCall:
return true
}
}
+func rewriteValueARM64_OpSlicemask(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (Slicemask <t> x)
+ // cond:
+ // result: (MVN (SRAconst <t> (SUBconst <t> x [1]) [63]))
+ for {
+ t := v.Type
+ x := v.Args[0]
+ v.reset(OpARM64MVN)
+ v0 := b.NewValue0(v.Line, OpARM64SRAconst, t)
+ v0.AuxInt = 63
+ v1 := b.NewValue0(v.Line, OpARM64SUBconst, t)
+ v1.AuxInt = 1
+ v1.AddArg(x)
+ v0.AddArg(v1)
+ v.AddArg(v0)
+ return true
+ }
+}
func rewriteValueARM64_OpSqrt(v *Value, config *Config) bool {
b := v.Block
_ = b
return rewriteValueMIPS64_OpSignExt8to32(v, config)
case OpSignExt8to64:
return rewriteValueMIPS64_OpSignExt8to64(v, config)
+ case OpSlicemask:
+ return rewriteValueMIPS64_OpSlicemask(v, config)
case OpStaticCall:
return rewriteValueMIPS64_OpStaticCall(v, config)
case OpStore:
_ = b
// match: (Not x)
// cond:
- // result: (XOR (MOVVconst [1]) x)
+ // result: (XORconst [1] x)
for {
x := v.Args[0]
- v.reset(OpMIPS64XOR)
- v0 := b.NewValue0(v.Line, OpMIPS64MOVVconst, config.fe.TypeUInt64())
- v0.AuxInt = 1
- v.AddArg(v0)
+ v.reset(OpMIPS64XORconst)
+ v.AuxInt = 1
v.AddArg(x)
return true
}
return true
}
}
+func rewriteValueMIPS64_OpSlicemask(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (Slicemask <t> x)
+ // cond:
+ // result: (NORconst [0] (SRAVconst <t> (SUBVconst <t> x [1]) [63]))
+ for {
+ t := v.Type
+ x := v.Args[0]
+ v.reset(OpMIPS64NORconst)
+ v.AuxInt = 0
+ v0 := b.NewValue0(v.Line, OpMIPS64SRAVconst, t)
+ v0.AuxInt = 63
+ v1 := b.NewValue0(v.Line, OpMIPS64SUBVconst, t)
+ v1.AuxInt = 1
+ v1.AddArg(x)
+ v0.AddArg(v1)
+ v.AddArg(v0)
+ return true
+ }
+}
func rewriteValueMIPS64_OpStaticCall(v *Value, config *Config) bool {
b := v.Block
_ = b
return rewriteValuePPC64_OpSignExt8to32(v, config)
case OpSignExt8to64:
return rewriteValuePPC64_OpSignExt8to64(v, config)
+ case OpSlicemask:
+ return rewriteValuePPC64_OpSlicemask(v, config)
case OpSqrt:
return rewriteValuePPC64_OpSqrt(v, config)
case OpStaticCall:
return true
}
}
+func rewriteValuePPC64_OpSlicemask(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (Slicemask <t> x)
+ // cond:
+ // result: (XORconst [-1] (SRADconst <t> (ADDconst <t> x [-1]) [63]))
+ for {
+ t := v.Type
+ x := v.Args[0]
+ v.reset(OpPPC64XORconst)
+ v.AuxInt = -1
+ v0 := b.NewValue0(v.Line, OpPPC64SRADconst, t)
+ v0.AuxInt = 63
+ v1 := b.NewValue0(v.Line, OpPPC64ADDconst, t)
+ v1.AuxInt = -1
+ v1.AddArg(x)
+ v0.AddArg(v1)
+ v.AddArg(v0)
+ return true
+ }
+}
func rewriteValuePPC64_OpSqrt(v *Value, config *Config) bool {
b := v.Block
_ = b
return rewriteValueS390X_OpSignExt8to32(v, config)
case OpSignExt8to64:
return rewriteValueS390X_OpSignExt8to64(v, config)
+ case OpSlicemask:
+ return rewriteValueS390X_OpSlicemask(v, config)
case OpSqrt:
return rewriteValueS390X_OpSqrt(v, config)
case OpStaticCall:
return true
}
}
+func rewriteValueS390X_OpSlicemask(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (Slicemask <t> x)
+ // cond:
+ // result: (XOR (MOVDconst [-1]) (SRADconst <t> (SUBconst <t> x [1]) [63]))
+ for {
+ t := v.Type
+ x := v.Args[0]
+ v.reset(OpS390XXOR)
+ v0 := b.NewValue0(v.Line, OpS390XMOVDconst, config.fe.TypeUInt64())
+ v0.AuxInt = -1
+ v.AddArg(v0)
+ v1 := b.NewValue0(v.Line, OpS390XSRADconst, t)
+ v1.AuxInt = 63
+ v2 := b.NewValue0(v.Line, OpS390XSUBconst, t)
+ v2.AuxInt = 1
+ v2.AddArg(x)
+ v1.AddArg(v2)
+ v.AddArg(v1)
+ return true
+ }
+}
func rewriteValueS390X_OpSqrt(v *Value, config *Config) bool {
b := v.Block
_ = b
return rewriteValuegeneric_OpSliceLen(v, config)
case OpSlicePtr:
return rewriteValuegeneric_OpSlicePtr(v, config)
+ case OpSlicemask:
+ return rewriteValuegeneric_OpSlicemask(v, config)
case OpSqrt:
return rewriteValuegeneric_OpSqrt(v, config)
case OpStore:
}
return false
}
+func rewriteValuegeneric_OpSlicemask(v *Value, config *Config) bool {
+ b := v.Block
+ _ = b
+ // match: (Slicemask (Const32 [x]))
+ // cond: x > 0
+ // result: (Const32 [-1])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConst32 {
+ break
+ }
+ x := v_0.AuxInt
+ if !(x > 0) {
+ break
+ }
+ v.reset(OpConst32)
+ v.AuxInt = -1
+ return true
+ }
+ // match: (Slicemask (Const32 [0]))
+ // cond:
+ // result: (Const32 [0])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConst32 {
+ break
+ }
+ if v_0.AuxInt != 0 {
+ break
+ }
+ v.reset(OpConst32)
+ v.AuxInt = 0
+ return true
+ }
+ // match: (Slicemask (Const64 [x]))
+ // cond: x > 0
+ // result: (Const64 [-1])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConst64 {
+ break
+ }
+ x := v_0.AuxInt
+ if !(x > 0) {
+ break
+ }
+ v.reset(OpConst64)
+ v.AuxInt = -1
+ return true
+ }
+ // match: (Slicemask (Const64 [0]))
+ // cond:
+ // result: (Const64 [0])
+ for {
+ v_0 := v.Args[0]
+ if v_0.Op != OpConst64 {
+ break
+ }
+ if v_0.AuxInt != 0 {
+ break
+ }
+ v.reset(OpConst64)
+ v.AuxInt = 0
+ return true
+ }
+ return false
+}
func rewriteValuegeneric_OpSqrt(v *Value, config *Config) bool {
b := v.Block
_ = b
z = (**x)[i:0:j] // ERROR "Disproved IsSliceInBounds$"
z = (**x)[0:i:j] // ERROR "Proved boolean IsSliceInBounds$"
z = (**x)[0:] // ERROR "slice: omit slice operation$"
- z = (**x)[2:8] // ERROR "Disproved Eq(32|64)$"
- z = (**x)[2:2] // ERROR "Disproved Eq(32|64)$" "Proved boolean IsSliceInBounds$"
- z = (**x)[0:i] // ERROR "Proved boolean IsSliceInBounds$"
- z = (**x)[2:i:8] // ERROR "Disproved IsSliceInBounds$" "Proved IsSliceInBounds$" "Proved boolean IsSliceInBounds$"
+ z = (**x)[2:8] // ERROR "Proved slicemask not needed$"
+ println(z)
+ z = (**x)[2:2]
+ z = (**x)[0:i]
+ z = (**x)[2:i:8] // ERROR "Disproved IsSliceInBounds$" "Proved IsSliceInBounds$"
z = (**x)[i:2:i] // ERROR "Proved IsSliceInBounds$" "Proved boolean IsSliceInBounds$"
z = z[0:i] // ERROR "Proved boolean IsSliceInBounds"