// boundsCheck generates bounds checking code. Checks if 0 <= idx < len, branches to exit if not.
// Starts a new block on return.
func (s *state) boundsCheck(idx, len *ssa.Value) {
+ if Debug['B'] != 0 {
+ return
+ }
// TODO: convert index to full width?
// TODO: if index is 64-bit and we're compiling to 32-bit, check that high 32 bits are zero.
b.Control = cmp
b.Likely = ssa.BranchLikely
bNext := s.f.NewBlock(ssa.BlockPlain)
+ bPanic := s.f.NewBlock(ssa.BlockPlain)
addEdge(b, bNext)
- addEdge(b, s.exit)
- // TODO: don't go directly to s.exit. Go to a stub that calls panicindex first.
+ addEdge(b, bPanic)
+ addEdge(bPanic, s.exit)
+ s.startBlock(bPanic)
+ // The panic check takes/returns memory to ensure that the right
+ // memory state is observed if the panic happens.
+ s.vars[&memvar] = s.newValue1(ssa.OpPanicIndexCheck, ssa.TypeMem, s.mem())
+ s.endBlock()
s.startBlock(bNext)
}
Warnl(int(v.Line), "generated nil check")
}
// Write to memory address 0. It doesn't matter what we write; use AX.
- // XORL AX, AX; MOVL AX, (AX) is shorter than MOVL AX, 0.
- // TODO: If we had the pointer (v.Args[0]) in a register r,
- // we could use MOVL AX, (r) instead of having to zero AX.
- // But it isn't worth loading r just to accomplish that.
- p := Prog(x86.AXORL)
- p.From.Type = obj.TYPE_REG
- p.From.Reg = x86.REG_AX
- p.To.Type = obj.TYPE_REG
- p.To.Reg = x86.REG_AX
+ // Input 0 is the pointer we just checked, use it as the destination.
+ r := regnum(v.Args[0])
q := Prog(x86.AMOVL)
q.From.Type = obj.TYPE_REG
q.From.Reg = x86.REG_AX
q.To.Type = obj.TYPE_MEM
- q.To.Reg = x86.REG_AX
+ q.To.Reg = r
+ // TODO: need AUNDEF here?
+ case ssa.OpAMD64LoweredPanicIndexCheck:
+ p := Prog(obj.ACALL)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Name = obj.NAME_EXTERN
+ p.To.Sym = Linksym(Panicindex.Sym)
+ // TODO: need AUNDEF here?
+ case ssa.OpAMD64LoweredPanicSliceCheck:
+ p := Prog(obj.ACALL)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Name = obj.NAME_EXTERN
+ p.To.Sym = Linksym(panicslice.Sym)
+ // TODO: need AUNDEF here?
case ssa.OpAMD64LoweredGetG:
r := regnum(v)
// See the comments in cmd/internal/obj/x86/obj6.go
func genBlock(b, next *ssa.Block, branches []branch) []branch {
lineno = b.Line
+
+ // after a panic call, don't emit any branch code
+ if len(b.Values) > 0 {
+ switch b.Values[len(b.Values)-1].Op {
+ case ssa.OpAMD64LoweredPanicNilCheck,
+ ssa.OpAMD64LoweredPanicIndexCheck,
+ ssa.OpAMD64LoweredPanicSliceCheck:
+ return branches
+ }
+ }
+
switch b.Kind {
case ssa.BlockPlain:
if b.Succs[0] != next {
(IsInBounds idx len) -> (SETB (CMPQ <TypeFlags> idx len))
(PanicNilCheck ptr mem) -> (LoweredPanicNilCheck ptr mem)
+(PanicIndexCheck mem) -> (LoweredPanicIndexCheck mem)
+(PanicSliceCheck mem) -> (LoweredPanicSliceCheck mem)
(GetG) -> (LoweredGetG)
(Move [size] dst src mem) -> (REPMOVSB dst src (MOVQconst <config.Frontend().TypeUInt64()> [size]) mem)
clobbers: dx | flags}
gp11hmul = regInfo{inputs: []regMask{ax, gpsp}, outputs: []regMask{dx},
clobbers: ax | flags}
+ gp10 = regInfo{inputs: []regMask{gp}}
gp2flags = regInfo{inputs: []regMask{gpsp, gpsp}, outputs: flagsonly}
gp1flags = regInfo{inputs: []regMask{gpsp}, outputs: flagsonly}
{name: "InvertFlags"}, // reverse direction of arg0
// Pseudo-ops
- {name: "LoweredPanicNilCheck"},
+ {name: "LoweredPanicNilCheck", reg: gp10},
+ {name: "LoweredPanicIndexCheck"},
+ {name: "LoweredPanicSliceCheck"},
{name: "LoweredGetG", reg: gp01},
}
{name: "IsInBounds"}, // 0 <= arg0 < arg1
// Pseudo-ops
- {name: "PanicNilCheck"}, // trigger a dereference fault; arg0=nil ptr, arg1=mem
- {name: "GetG"}, // runtime.getg() (read g pointer)
+ {name: "PanicNilCheck"}, // trigger a dereference fault; arg0=nil ptr, arg1=mem, returns mem
+ {name: "PanicIndexCheck"}, // trigger a bounds check failure, arg0=mem, returns mem
+ {name: "PanicSliceCheck"}, // trigger a slice bounds check failure, arg0=mem, returns mem
+ {name: "GetG"}, // runtime.getg() (read g pointer)
// Indexing operations
{name: "ArrayIndex"}, // arg0=array, arg1=index. Returns a[i]
OpAMD64REPMOVSB
OpAMD64InvertFlags
OpAMD64LoweredPanicNilCheck
+ OpAMD64LoweredPanicIndexCheck
+ OpAMD64LoweredPanicSliceCheck
OpAMD64LoweredGetG
OpAdd8
OpIsNonNil
OpIsInBounds
OpPanicNilCheck
+ OpPanicIndexCheck
+ OpPanicSliceCheck
OpGetG
OpArrayIndex
OpPtrIndex
},
{
name: "LoweredPanicNilCheck",
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 65519}, // .AX .CX .DX .BX .BP .SI .DI .R8 .R9 .R10 .R11 .R12 .R13 .R14 .R15
+ },
+ },
+ },
+ {
+ name: "LoweredPanicIndexCheck",
+ reg: regInfo{},
+ },
+ {
+ name: "LoweredPanicSliceCheck",
reg: regInfo{},
},
{
name: "PanicNilCheck",
generic: true,
},
+ {
+ name: "PanicIndexCheck",
+ generic: true,
+ },
+ {
+ name: "PanicSliceCheck",
+ generic: true,
+ },
{
name: "GetG",
generic: true,
goto end6f8a8c559a167d1f0a5901d09a1fb248
end6f8a8c559a167d1f0a5901d09a1fb248:
;
+ case OpPanicIndexCheck:
+ // match: (PanicIndexCheck mem)
+ // cond:
+ // result: (LoweredPanicIndexCheck mem)
+ {
+ mem := v.Args[0]
+ v.Op = OpAMD64LoweredPanicIndexCheck
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(mem)
+ return true
+ }
+ goto enda5014ba73d3550a5b66424044395c70f
+ enda5014ba73d3550a5b66424044395c70f:
+ ;
case OpPanicNilCheck:
// match: (PanicNilCheck ptr mem)
// cond:
goto enda02b1ad5a6f929b782190145f2c8628b
enda02b1ad5a6f929b782190145f2c8628b:
;
+ case OpPanicSliceCheck:
+ // match: (PanicSliceCheck mem)
+ // cond:
+ // result: (LoweredPanicSliceCheck mem)
+ {
+ mem := v.Args[0]
+ v.Op = OpAMD64LoweredPanicSliceCheck
+ v.AuxInt = 0
+ v.Aux = nil
+ v.resetArgs()
+ v.AddArg(mem)
+ return true
+ }
+ goto end238ed0074810b55bd2bba7b45cdeed68
+ end238ed0074810b55bd2bba7b45cdeed68:
+ ;
case OpRsh16Ux16:
// match: (Rsh16Ux16 <t> x y)
// cond: