// OpLoadROIntIndex32ScaleDisp must not allocate registers. func (mach X86) OpLoadROIntIndex32ScaleDisp(code gen.Coder, t types.T, reg regs.R, regZeroExt bool, scale uint8, addr int32) (resultZeroExt bool) { if !regZeroExt { Mov.opFromReg(code, types.I32, reg, reg) } Mov.opFromAddr(code, t, reg, scale, reg, code.RODataAddr()+addr) resultZeroExt = true return }
func (mach X86) opConvertUnsignedI64ToFloat(code gen.Coder, resultType types.T, inputReg regs.R) { // this algorithm is copied from code generated by gcc and clang: var done links.L var huge links.L // TODO: allocate target reg Test.opFromReg(code, types.I64, inputReg, inputReg) Js.rel8.opStub(code) huge.AddSite(code.Len()) // max. 63-bit value Cvtsi2sSSE.opReg(code, resultType, types.I64, regResult, inputReg) JmpRel.rel8.opStub(code) done.AddSite(code.Len()) huge.Addr = code.Len() mach.updateBranches8(code, &huge) // 64-bit value Mov.opFromReg(code, types.I64, regScratch, inputReg) And.opImm(code, types.I64, regScratch, 1) ShrImm.op(code, types.I64, inputReg, 1) Or.opFromReg(code, types.I64, inputReg, regScratch) Cvtsi2sSSE.opReg(code, resultType, types.I64, regResult, inputReg) AddsSSE.opFromReg(code, resultType, regResult, regResult) done.Addr = code.Len() mach.updateBranches8(code, &done) }
// OpStoreStack must not allocate registers. func (mach X86) OpStoreStack(code gen.Coder, offset int32, x values.Operand) { var reg regs.R if x.Storage.IsReg() { reg = x.Reg() } else { reg = regScratch mach.OpMove(code, reg, x, true) } mach.OpStoreStackReg(code, x.Type, offset, reg) if x.Storage == values.TempReg { code.FreeReg(x.Type, reg) } }
// OpPush must not allocate registers, and must not update CPU's condition // flags unless the operand is the condition flags. func (mach X86) OpPush(code gen.Coder, x values.Operand) { var reg regs.R switch { case x.Storage.IsReg(): reg = x.Reg() case x.Storage == values.Imm: value := x.ImmValue() switch { case value >= -0x80 && value < 0x80: PushImm8.op(code, imm{int8(value)}) return case value >= -0x80000000 && value < 0x80000000: PushImm32.op(code, imm{int32(value)}) return } fallthrough default: reg = regScratch mach.OpMove(code, reg, x, true) } switch x.Type.Category() { case types.Int: Push.op(code, reg) case types.Float: pushFloatOp(code, x.Type, reg) default: panic(x) } if x.Storage == values.TempReg { code.FreeReg(x.Type, reg) } }
func (mach X86) OpBranchIf(code gen.Coder, x values.Operand, yes bool, addr int32) (sites []int32) { var cond values.Condition if x.Storage == values.ConditionFlags { cond = x.Condition() } else { reg, _, own := mach.opBorrowMaybeScratchReg(code, x, false) if own { defer code.FreeReg(types.I32, reg) } Test.opFromReg(code, types.I32, reg, reg) cond = values.Ne } if !yes { cond = values.InvertedConditions[cond] } var end links.L switch { case cond >= values.MinUnorderedOrCondition: Jp.op(code, addr) sites = append(sites, code.Len()) case cond >= values.MinOrderedAndCondition: Jp.rel8.opStub(code) end.AddSite(code.Len()) } conditionInsns[cond].jcc.op(code, addr) sites = append(sites, code.Len()) end.Addr = code.Len() mach.updateBranches8(code, &end) return }
func (mach X86) OpTrapIfStackExhausted(code gen.Coder) (stackCheckAddr int32) { var checked links.L Lea.opFromStack(code, types.I64, regScratch, -0x80000000) // reserve 32-bit displacement stackCheckAddr = code.Len() Cmp.opFromReg(code, types.I64, regScratch, regStackLimit) Jge.rel8.opStub(code) checked.AddSite(code.Len()) code.OpTrapCall(traps.CallStackExhausted) checked.Addr = code.Len() mach.updateBranches8(code, &checked) return }
func (mach X86) OpCall(code gen.Coder, addr int32) (retAddr int32) { if addr == 0 { if Native { // address slot must be aligned if relPos := (code.Len() + CallRel.size()) & 3; relPos > 0 { padSize := 4 - relPos code.Write(nopSequences[padSize-1]) } } CallRel.opMissingFunction(code) } else { CallRel.op(code, addr) } return code.Len() }
// OpBranchIfOutOfBounds must not allocate registers. indexReg will be // zero-extended. func (mach X86) OpBranchIfOutOfBounds(code gen.Coder, indexReg regs.R, upperBound, addr int32) int32 { mach.opCompareBounds(code, indexReg, upperBound) Jle.op(code, addr) // TODO: is this the correct comparison? return code.Len() }
func (mach X86) OpBranch(code gen.Coder, addr int32) int32 { JmpRel.op(code, addr) return code.Len() }
// OpMove must not update CPU's condition flags if preserveFlags is set. // // X86 implementation note: must not blindly rely on regScratch or regResult in // this function because we may be moving to one of them. func (mach X86) OpMove(code gen.Coder, targetReg regs.R, x values.Operand, preserveFlags bool) (zeroExt bool) { switch x.Type.Category() { case types.Int: switch x.Storage { case values.Imm: if value := x.ImmValue(); value == 0 && !preserveFlags { Xor.opFromReg(code, types.I32, targetReg, targetReg) } else { MovImm64.op(code, x.Type, targetReg, value) } zeroExt = true case values.VarMem: Mov.opFromStack(code, x.Type, targetReg, x.VarMemOffset()) zeroExt = true case values.VarReg: if sourceReg := x.Reg(); sourceReg != targetReg { Mov.opFromReg(code, x.Type, targetReg, sourceReg) zeroExt = true } case values.TempReg: if sourceReg := x.Reg(); sourceReg != targetReg { Mov.opFromReg(code, x.Type, targetReg, sourceReg) zeroExt = true } else if targetReg == regResult { zeroExt = x.RegZeroExt() } else { panic("moving temporary integer register to itself") } case values.Stack: Pop.op(code, targetReg) case values.ConditionFlags: if x.Type != types.I32 { panic(x) } var end links.L cond := x.Condition() setcc := conditionInsns[cond].setcc switch { case cond >= values.MinUnorderedOrCondition: MovImm.opImm(code, x.Type, targetReg, 1) // true Jp.rel8.opStub(code) // if unordered, else end.AddSite(code.Len()) // setcc.opReg(code, targetReg) // cond case cond >= values.MinOrderedAndCondition: MovImm.opImm(code, x.Type, targetReg, 0) // false Jp.rel8.opStub(code) // if unordered, else end.AddSite(code.Len()) // setcc.opReg(code, targetReg) // cond default: setcc.opReg(code, targetReg) Movzx8.opFromReg(code, x.Type, targetReg, targetReg) } end.Addr = code.Len() mach.updateBranches8(code, &end) zeroExt = true default: panic(x) } case types.Float: switch x.Storage { case values.Imm: if value := x.ImmValue(); value == 0 { PxorSSE.opFromReg(code, x.Type, targetReg, targetReg) } else { MovImm64.op(code, x.Type, regScratch, value) // integer scratch register MovSSE.opFromReg(code, x.Type, targetReg, regScratch) } case values.VarMem: MovsSSE.opFromStack(code, x.Type, targetReg, x.VarMemOffset()) case values.VarReg: if sourceReg := x.Reg(); sourceReg != targetReg { MovsSSE.opFromReg(code, x.Type, targetReg, sourceReg) } case values.TempReg: if sourceReg := x.Reg(); sourceReg != targetReg { MovsSSE.opFromReg(code, x.Type, targetReg, sourceReg) } else if targetReg != regResult { panic("moving temporary float register to itself") } case values.Stack: popFloatOp(code, x.Type, targetReg) default: panic(x) } default: panic(x) } code.Consumed(x) return }
// OpCallIndirect using table index located in result register. func (mach X86) OpCallIndirect(code gen.Coder, tableLen, sigIndex int32) int32 { var outOfBounds links.L var checksOut links.L mach.opCompareBounds(code, regResult, tableLen) Jle.rel8.opStub(code) // TODO: is this the correct comparison? outOfBounds.AddSite(code.Len()) Mov.opFromAddr(code, types.I64, regResult, 3, regResult, code.RODataAddr()+gen.ROTableAddr) Mov.opFromReg(code, types.I32, regScratch, regResult) // zero-extended function address ShrImm.op(code, types.I64, regResult, 32) // signature index Cmp.opImm(code, types.I32, regResult, sigIndex) Je.rel8.opStub(code) checksOut.AddSite(code.Len()) code.OpTrapCall(traps.IndirectCallSignature) outOfBounds.Addr = code.Len() mach.updateBranches8(code, &outOfBounds) code.OpTrapCall(traps.IndirectCallIndex) checksOut.Addr = code.Len() mach.updateBranches8(code, &checksOut) Add.opFromReg(code, types.I64, regScratch, regTextBase) Call.opReg(code, regScratch) return code.Len() }