github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/backend/isa/amd64/abi.go (about) 1 package amd64 2 3 import ( 4 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend" 5 "github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc" 6 "github.com/tetratelabs/wazero/internal/engine/wazevo/ssa" 7 ) 8 9 // For the details of the ABI, see: 10 // https://github.com/golang/go/blob/49d42128fd8594c172162961ead19ac95e247d24/src/cmd/compile/abi-internal.md#amd64-architecture 11 12 var ( 13 intArgResultRegs = []regalloc.RealReg{rax, rbx, rcx, rdi, rsi, r8, r9, r10, r11} 14 floatArgResultRegs = []regalloc.RealReg{xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7} 15 ) 16 17 var regInfo = ®alloc.RegisterInfo{ 18 AllocatableRegisters: [regalloc.NumRegType][]regalloc.RealReg{ 19 regalloc.RegTypeInt: { 20 rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15, 21 }, 22 regalloc.RegTypeFloat: { 23 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, 24 }, 25 }, 26 CalleeSavedRegisters: regalloc.NewRegSet( 27 rdx, r12, r13, r14, r15, 28 xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, 29 ), 30 CallerSavedRegisters: regalloc.NewRegSet( 31 rax, rcx, rbx, rsi, rdi, r8, r9, r10, r11, 32 xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, 33 ), 34 RealRegToVReg: []regalloc.VReg{ 35 rax: raxVReg, rcx: rcxVReg, rdx: rdxVReg, rbx: rbxVReg, rsp: rspVReg, rbp: rbpVReg, rsi: rsiVReg, rdi: rdiVReg, 36 r8: r8VReg, r9: r9VReg, r10: r10VReg, r11: r11VReg, r12: r12VReg, r13: r13VReg, r14: r14VReg, r15: r15VReg, 37 xmm0: xmm0VReg, xmm1: xmm1VReg, xmm2: xmm2VReg, xmm3: xmm3VReg, xmm4: xmm4VReg, xmm5: xmm5VReg, xmm6: xmm6VReg, 38 xmm7: xmm7VReg, xmm8: xmm8VReg, xmm9: xmm9VReg, xmm10: xmm10VReg, xmm11: xmm11VReg, xmm12: xmm12VReg, 39 xmm13: xmm13VReg, xmm14: xmm14VReg, xmm15: xmm15VReg, 40 }, 41 RealRegName: func(r regalloc.RealReg) string { return regNames[r] }, 42 RealRegType: func(r regalloc.RealReg) regalloc.RegType { 43 if r < xmm0 { 44 return regalloc.RegTypeInt 45 } 46 return regalloc.RegTypeFloat 47 }, 48 } 49 50 // ArgsResultsRegs implements backend.Machine. 51 func (m *machine) ArgsResultsRegs() (argResultInts, argResultFloats []regalloc.RealReg) { 52 return intArgResultRegs, floatArgResultRegs 53 } 54 55 // LowerParams implements backend.Machine. 56 func (m *machine) LowerParams(args []ssa.Value) { 57 a := m.currentABI 58 59 for i, ssaArg := range args { 60 if !ssaArg.Valid() { 61 continue 62 } 63 reg := m.c.VRegOf(ssaArg) 64 arg := &a.Args[i] 65 if arg.Kind == backend.ABIArgKindReg { 66 m.InsertMove(reg, arg.Reg, arg.Type) 67 } else { 68 // 69 // (high address) 70 // +-----------------+ 71 // | ....... | 72 // | ret Y | 73 // | ....... | 74 // | ret 0 | 75 // | arg X | 76 // | ....... | 77 // | arg 1 | 78 // | arg 0 | 79 // | ReturnAddress | 80 // | Caller_RBP | 81 // +-----------------+ <-- RBP 82 // | ........... | 83 // | clobbered M | 84 // | ............ | 85 // | clobbered 0 | 86 // | spill slot N | 87 // | ........... | 88 // | spill slot 0 | 89 // RSP--> +-----------------+ 90 // (low address) 91 92 // Load the value from the arg stack slot above the current RBP. 93 load := m.allocateInstr() 94 mem := newOperandMem(m.newAmodeImmRBPReg(uint32(arg.Offset + 16))) 95 switch arg.Type { 96 case ssa.TypeI32: 97 load.asMovzxRmR(extModeLQ, mem, reg) 98 case ssa.TypeI64: 99 load.asMov64MR(mem, reg) 100 case ssa.TypeF32: 101 load.asXmmUnaryRmR(sseOpcodeMovss, mem, reg) 102 case ssa.TypeF64: 103 load.asXmmUnaryRmR(sseOpcodeMovsd, mem, reg) 104 case ssa.TypeV128: 105 load.asXmmUnaryRmR(sseOpcodeMovdqu, mem, reg) 106 default: 107 panic("BUG") 108 } 109 m.insert(load) 110 } 111 } 112 } 113 114 // LowerReturns implements backend.Machine. 115 func (m *machine) LowerReturns(rets []ssa.Value) { 116 // Load the XMM registers first as it might need a temporary register to inline 117 // constant return. 118 a := m.currentABI 119 for i, ret := range rets { 120 r := &a.Rets[i] 121 if !r.Type.IsInt() { 122 m.LowerReturn(ret, r) 123 } 124 } 125 // Then load the GPR registers. 126 for i, ret := range rets { 127 r := &a.Rets[i] 128 if r.Type.IsInt() { 129 m.LowerReturn(ret, r) 130 } 131 } 132 } 133 134 func (m *machine) LowerReturn(ret ssa.Value, r *backend.ABIArg) { 135 reg := m.c.VRegOf(ret) 136 if def := m.c.ValueDefinition(ret); def.IsFromInstr() { 137 // Constant instructions are inlined. 138 if inst := def.Instr; inst.Constant() { 139 m.insertLoadConstant(inst, reg) 140 } 141 } 142 if r.Kind == backend.ABIArgKindReg { 143 m.InsertMove(r.Reg, reg, ret.Type()) 144 } else { 145 // 146 // (high address) 147 // +-----------------+ 148 // | ....... | 149 // | ret Y | 150 // | ....... | 151 // | ret 0 | 152 // | arg X | 153 // | ....... | 154 // | arg 1 | 155 // | arg 0 | 156 // | ReturnAddress | 157 // | Caller_RBP | 158 // +-----------------+ <-- RBP 159 // | ........... | 160 // | clobbered M | 161 // | ............ | 162 // | clobbered 0 | 163 // | spill slot N | 164 // | ........... | 165 // | spill slot 0 | 166 // RSP--> +-----------------+ 167 // (low address) 168 169 // Store the value to the return stack slot above the current RBP. 170 store := m.allocateInstr() 171 mem := newOperandMem(m.newAmodeImmRBPReg(uint32(m.currentABI.ArgStackSize + 16 + r.Offset))) 172 switch r.Type { 173 case ssa.TypeI32: 174 store.asMovRM(reg, mem, 4) 175 case ssa.TypeI64: 176 store.asMovRM(reg, mem, 8) 177 case ssa.TypeF32: 178 store.asXmmMovRM(sseOpcodeMovss, reg, mem) 179 case ssa.TypeF64: 180 store.asXmmMovRM(sseOpcodeMovsd, reg, mem) 181 case ssa.TypeV128: 182 store.asXmmMovRM(sseOpcodeMovdqu, reg, mem) 183 } 184 m.insert(store) 185 } 186 }