func (mach X86) binaryIntCompareOp(code gen.RegCoder, cond uint8, a, b values.Operand) (result values.Operand) { result = values.ConditionFlagsOperand(values.Condition(cond)) targetReg, _, own := mach.opBorrowMaybeResultReg(code, a, false) if own { defer code.FreeReg(a.Type, targetReg) } if b.Storage == values.VarMem { Cmp.opFromStack(code, a.Type, targetReg, b.VarMemOffset()) return } var sourceReg regs.R if b.Storage.IsReg() { sourceReg = b.Reg() } else { if b.Storage == values.Imm { if value := b.ImmValue(); value >= -0x80000000 && value < 0x80000000 { Cmp.opImm(code, a.Type, targetReg, int32(value)) return } } sourceReg = regScratch mach.OpMove(code, sourceReg, b, false) } Cmp.opFromReg(code, a.Type, targetReg, sourceReg) code.Consumed(b) return }
// LoadOp makes sure that index gets zero-extended if it's a VarReg operand. func (mach X86) LoadOp(code gen.RegCoder, oper uint16, index values.Operand, resultType types.T, offset uint32) (result values.Operand) { size := oper >> 8 baseReg, indexReg, ownIndexReg, disp := mach.opMemoryAddress(code, size, index, offset) if ownIndexReg { defer code.FreeReg(types.I64, indexReg) } load := memoryLoads[uint8(oper)] targetReg, ok := code.TryAllocReg(resultType) if !ok { targetReg = regResult } result = values.TempRegOperand(resultType, targetReg, load.zeroExt) insnType := load.insnType if insnType == 0 { insnType = resultType } load.insn.opFromIndirect(code, insnType, targetReg, 0, indexReg, baseReg, disp) return }
func (mach X86) binaryFloatOp(code gen.RegCoder, index uint8, a, b values.Operand) values.Operand { targetReg, _ := mach.opMaybeResultReg(code, a, false) sourceReg, _, own := mach.opBorrowMaybeScratchReg(code, b, false) if own { defer code.FreeReg(b.Type, sourceReg) } binaryFloatInsns[index].opFromReg(code, a.Type, targetReg, sourceReg) return values.TempRegOperand(a.Type, targetReg, false) }
func (mach X86) binaryFloatCompareOp(code gen.RegCoder, cond uint8, a, b values.Operand) values.Operand { aReg, _, own := mach.opBorrowMaybeResultReg(code, a, true) if own { defer code.FreeReg(a.Type, aReg) } bReg, _, own := mach.opBorrowMaybeScratchReg(code, b, false) if own { defer code.FreeReg(b.Type, bReg) } UcomisSSE.opFromReg(code, a.Type, aReg, bReg) return values.ConditionFlagsOperand(values.Condition(cond)) }
func (mach X86) unaryIntOp(code gen.RegCoder, index uint8, x values.Operand) (result values.Operand) { if index == opers.IndexIntEqz { reg, _, own := mach.opBorrowMaybeScratchReg(code, x, false) if own { defer code.FreeReg(x.Type, reg) } Test.opFromReg(code, x.Type, reg, reg) return values.ConditionFlagsOperand(values.Eq) } var targetReg regs.R sourceReg, _, own := mach.opBorrowMaybeScratchReg(code, x, false) if own { targetReg = sourceReg } else { var ok bool targetReg, ok = code.TryAllocReg(x.Type) if !ok { targetReg = regResult } } result = values.TempRegOperand(x.Type, targetReg, true) insn := unaryIntInsns[index] switch index { case opers.IndexIntClz: insn.opFromReg(code, x.Type, regScratch, sourceReg) MovImm.opImm(code, x.Type, targetReg, -1) Cmove.opFromReg(code, x.Type, regScratch, targetReg) MovImm.opImm(code, x.Type, targetReg, (int32(x.Type.Size())<<3)-1) Sub.opFromReg(code, x.Type, targetReg, regScratch) case opers.IndexIntCtz: insn.opFromReg(code, x.Type, targetReg, sourceReg) MovImm.opImm(code, x.Type, regScratch, int32(x.Type.Size())<<3) Cmove.opFromReg(code, x.Type, targetReg, regScratch) case opers.IndexIntPopcnt: insn.opFromReg(code, x.Type, targetReg, sourceReg) } return }
func (mach X86) binaryShiftOp(code gen.RegCoder, index uint8, a, b values.Operand) values.Operand { insn := binaryShiftInsns[index] var targetReg regs.R switch b.Storage { case values.Imm: targetReg, _ = mach.opMaybeResultReg(code, a, true) insn.imm.op(code, b.Type, targetReg, uint8(b.ImmValue())) default: if b.Storage.IsReg() && b.Reg() == regShiftCount { targetReg, _ = mach.opMaybeResultReg(code, a, false) defer code.Discard(b) } else { if code.RegAllocated(types.I32, regShiftCount) { targetReg, _ = mach.opMaybeResultReg(code, a, true) if targetReg == regShiftCount { Mov.opFromReg(code, a.Type, regResult, regShiftCount) targetReg = regResult defer code.FreeReg(types.I32, regShiftCount) } else { // unknown operand in regShiftCount Mov.opFromReg(code, types.I64, regScratch, regShiftCount) defer Mov.opFromReg(code, types.I64, regShiftCount, regScratch) } } else { code.AllocSpecificReg(types.I32, regShiftCount) defer code.FreeReg(types.I32, regShiftCount) targetReg, _ = mach.opMaybeResultReg(code, a, true) } b.Type = types.I32 // TODO: 8-bit mov mach.OpMove(code, regShiftCount, b, false) } insn.opReg(code, a.Type, targetReg) } return values.TempRegOperand(a.Type, targetReg, true) }
func (mach X86) ConversionOp(code gen.RegCoder, oper uint16, resultType types.T, source values.Operand) (result values.Operand) { if oper == opers.Wrap { source.Type = types.I32 // short mov; useful zeroExt flag reg, zeroExt := mach.opMaybeResultReg(code, source, false) return values.TempRegOperand(resultType, reg, zeroExt) } reg, zeroExt := mach.opMaybeResultReg(code, source, false) // TODO: for int<->float ops: borrow source reg, allocate target reg switch oper { case opers.ExtendS: Movsxd.opFromReg(code, 0, reg, reg) result = values.TempRegOperand(resultType, reg, false) case opers.ExtendU: if !zeroExt { Mov.opFromReg(code, types.I32, reg, reg) } result = values.TempRegOperand(resultType, reg, false) case opers.Mote: Cvts2sSSE.opFromReg(code, source.Type, reg, reg) result = values.TempRegOperand(resultType, reg, false) case opers.TruncS: panic(fmt.Errorf("%s.trunc_s/%s not implemented", resultType, source.Type)) case opers.TruncU: panic(fmt.Errorf("%s.trunc_u/%s not implemented", resultType, source.Type)) case opers.ConvertS: Cvtsi2sSSE.opReg(code, resultType, source.Type, regResult, reg) code.FreeReg(source.Type, reg) result = values.TempRegOperand(resultType, regResult, false) case opers.ConvertU: if source.Type == types.I32 { if !zeroExt { Mov.opFromReg(code, types.I32, reg, reg) } Cvtsi2sSSE.opReg(code, resultType, types.I64, regResult, reg) } else { mach.opConvertUnsignedI64ToFloat(code, resultType, reg) } code.FreeReg(source.Type, reg) result = values.TempRegOperand(resultType, regResult, false) case opers.Reinterpret: if source.Type.Category() == types.Int { MovSSE.opFromReg(code, source.Type, regResult, reg) } else { MovSSE.opToReg(code, source.Type, regResult, reg) } code.FreeReg(source.Type, reg) result = values.TempRegOperand(resultType, regResult, true) } return }
func (mach X86) OpSelect(code gen.RegCoder, a, b, condOperand values.Operand) values.Operand { defer code.Consumed(condOperand) var cond values.Condition switch condOperand.Storage { case values.VarMem: Cmp.opImmToStack(code, types.I32, condOperand.VarMemOffset(), 0) cond = values.Ne case values.VarReg, values.TempReg: reg := condOperand.Reg() Test.opFromReg(code, types.I32, reg, reg) cond = values.Ne case values.Stack: mach.OpAddImmToStackPtr(code, 8) // do before cmp to avoid overwriting flags Cmp.opImmToStack(code, types.I32, -8, 0) cond = values.Ne case values.ConditionFlags: cond = condOperand.Condition() case values.Imm: if condOperand.ImmValue() != 0 { code.Consumed(b) return a } else { code.Consumed(a) return b } default: panic(condOperand) } t := a.Type targetReg, _ := mach.opMaybeResultReg(code, b, true) switch t.Category() { case types.Int: cmov := conditionInsns[cond].cmov switch a.Storage { case values.VarMem: cmov.opFromStack(code, t, targetReg, a.VarMemOffset()) default: aReg, _, own := mach.opBorrowMaybeScratchReg(code, a, true) if own { defer code.FreeReg(t, aReg) } cmov.opFromReg(code, t, targetReg, aReg) } case types.Float: var moveIt links.L var end links.L cond = values.InvertedConditions[cond] notCondJump := conditionInsns[cond].jcc switch { case cond >= values.MinUnorderedOrCondition: Jp.rel8.opStub(code) // move it if unordered moveIt.AddSite(code.Len()) notCondJump.rel8.opStub(code) // break if not cond end.AddSite(code.Len()) case cond >= values.MinOrderedAndCondition: Jp.rel8.opStub(code) // break if unordered end.AddSite(code.Len()) notCondJump.rel8.opStub(code) // break if not cond end.AddSite(code.Len()) default: notCondJump.rel8.opStub(code) // break if not cond end.AddSite(code.Len()) } moveIt.Addr = code.Len() mach.updateBranches8(code, &moveIt) mach.OpMove(code, targetReg, a, false) end.Addr = code.Len() mach.updateBranches8(code, &end) default: panic(t) } // cmov zero-extends the target unconditionally return values.TempRegOperand(t, targetReg, true) }
// StoreOp makes sure that index gets zero-extended if it's a VarReg operand. func (mach X86) StoreOp(code gen.RegCoder, oper uint16, index, x values.Operand, offset uint32) { size := oper >> 8 baseReg, indexReg, ownIndexReg, disp := mach.opMemoryAddress(code, size, index, offset) if ownIndexReg { defer code.FreeReg(types.I64, indexReg) } store := memoryStores[uint8(oper)] insnType := store.insnType if insnType == 0 { insnType = x.Type } if x.Storage == values.Imm { value := x.ImmValue() value32 := int32(value) switch { case size == 1: value32 = int32(int8(value32)) case size == 2: value32 = int32(int16(value32)) case size == 4 || (value >= -0x80000000 && value < 0x80000000): default: goto large } store.insn.opImmToIndirect(code, insnType, 0, indexReg, baseReg, disp, value32) return large: } valueReg, _, own := mach.opBorrowMaybeResultReg(code, x, false) if own { defer code.FreeReg(x.Type, valueReg) } store.insn.opToIndirect(code, insnType, valueReg, 0, indexReg, baseReg, disp) } // opMemoryAddress may return the scratch register as the base. func (mach X86) opMemoryAddress(code gen.RegCoder, size uint16, index values.Operand, offset uint32) (baseReg, indexReg regs.R, ownIndexReg bool, disp int32) { sizeReach := uint64(size - 1) reachOffset := uint64(offset) + sizeReach if reachOffset >= 0x80000000 { code.OpTrapCall(traps.MemoryOutOfBounds) return } alreadyChecked := reachOffset < uint64(index.Bounds.Upper) switch index.Storage { case values.Imm: value := uint64(index.ImmValue()) if value >= 0x80000000 { code.OpTrapCall(traps.MemoryOutOfBounds) return } addr := value + uint64(offset) reachAddr := addr + sizeReach if reachAddr >= 0x80000000 { code.OpTrapCall(traps.MemoryOutOfBounds) return } if reachAddr < uint64(code.MinMemorySize()) || alreadyChecked { baseReg = regMemoryBase indexReg = NoIndex disp = int32(addr) return } Lea.opFromIndirect(code, types.I64, regScratch, 0, NoIndex, regMemoryBase, int32(reachAddr)) default: reg, zeroExt, own := mach.opBorrowMaybeScratchReg(code, index, true) if !zeroExt { Mov.opFromReg(code, types.I32, reg, reg) // zero-extend index } if alreadyChecked { baseReg = regMemoryBase indexReg = reg ownIndexReg = own disp = int32(offset) return } Lea.opFromIndirect(code, types.I64, regScratch, 0, reg, regMemoryBase, int32(reachOffset)) if own { code.FreeReg(types.I32, reg) } } Cmp.opFromReg(code, types.I64, regScratch, regMemoryLimit) if addr := code.TrapTrampolineAddr(traps.MemoryOutOfBounds); addr != 0 { Jge.op(code, addr) } else { var checked links.L Jl.rel8.opStub(code) checked.AddSite(code.Len()) code.OpTrapCall(traps.MemoryOutOfBounds) checked.Addr = code.Len() mach.updateBranches8(code, &checked) } baseReg = regScratch indexReg = NoIndex disp = -int32(sizeReach) return } func (mach X86) OpCurrentMemory(code gen.RegCoder) values.Operand { Mov.opFromReg(code, types.I64, regResult, regMemoryLimit) Sub.opFromReg(code, types.I64, regResult, regMemoryBase) ShrImm.op(code, types.I64, regResult, wasm.PageBits) return values.TempRegOperand(types.I32, regResult, true) } func (mach X86) OpGrowMemory(code gen.RegCoder, x values.Operand) values.Operand { var out links.L var fail links.L MovMMX.opToReg(code, types.I64, regScratch, regMemoryGrowLimitMMX) targetReg, zeroExt := mach.opMaybeResultReg(code, x, false) if !zeroExt { Mov.opFromReg(code, types.I32, targetReg, targetReg) } ShlImm.op(code, types.I64, targetReg, wasm.PageBits) Add.opFromReg(code, types.I64, targetReg, regMemoryLimit) // new memory limit Cmp.opFromReg(code, types.I64, targetReg, regScratch) Jg.rel8.opStub(code) fail.AddSite(code.Len()) Mov.opFromReg(code, types.I64, regScratch, regMemoryLimit) Mov.opFromReg(code, types.I64, regMemoryLimit, targetReg) Sub.opFromReg(code, types.I64, regScratch, regMemoryBase) ShrImm.op(code, types.I64, regScratch, wasm.PageBits) // value on success Mov.opFromReg(code, types.I32, targetReg, regScratch) JmpRel.rel8.opStub(code) out.AddSite(code.Len()) fail.Addr = code.Len() mach.updateBranches8(code, &fail) MovImm.opImm(code, types.I32, targetReg, -1) // value on failure out.Addr = code.Len() mach.updateBranches8(code, &out) return values.TempRegOperand(types.I32, targetReg, true) }