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 = &regalloc.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  }