1/*
2 * gomacro - A Go interpreter with Lisp-like macros
3 *
4 * Copyright (C) 2018-2019 Massimiliano Ghilardi
5 *
6 *     This Source Code Form is subject to the terms of the Mozilla Public
7 *     License, v. 2.0. If a copy of the MPL was not distributed with this
8 *     file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 *
11 * arch.go
12 *
13 *  Created on May 26, 2018
14 *      Author Massimiliano Ghilardi
15 */
16
17package arm64
18
19import (
20	"fmt"
21
22	"github.com/cosmos72/gomacro/jit/common"
23)
24
25type Arm64 struct {
26}
27
28func init() {
29	common.Archs[ARM64] = Arm64{}
30}
31
32// implement Arch interface
33func (Arm64) Id() ArchId {
34	return ARM64
35}
36
37func (Arm64) String() string {
38	return NAME
39}
40
41func (Arm64) RegIdConfig() RegIdConfig {
42	return RegIdConfig{
43		RLo:         RLo,
44		RHi:         RHi,
45		RSP:         RSP,
46		RVAR:        RVAR,
47		RAllocFirst: RLo,
48	}
49}
50
51func (Arm64) RegIdValid(id RegId) bool {
52	return id >= RLo && id < RHi // XZR/XSP is valid only in few, hand-checked cases
53}
54
55func (Arm64) RegIdString(id RegId) string {
56	var s string
57	if id >= RLo && id <= RHi {
58		s = regName8[id]
59	}
60	if s == "" {
61		s = fmt.Sprintf("unknown_reg(%#x)", uint8(id))
62	}
63	return s
64}
65
66func (Arm64) RegValid(r Reg) bool {
67	// XZR/XSP is valid only in few, hand-checked cases
68	return r.RegId().Valid() && r.Kind().Size() != 0
69}
70
71func (Arm64) RegString(r Reg) string {
72	var s string
73	id := r.RegId()
74	if id >= RLo && id <= RHi {
75		switch r.Kind().Size() {
76		case 1, 2, 4:
77			s = regName4[id]
78		case 8:
79			s = regName8[id]
80		}
81	}
82	if s == "" {
83		s = fmt.Sprintf("unknown_reg(%#x,%v)", uint8(id), r.Kind())
84	}
85	return s
86}
87
88func (arch Arm64) MemString(m Mem) string {
89	var regstr string
90	regid := m.RegId()
91	if regid == XZR {
92		regstr = "xsp"
93	} else {
94		regstr = arch.RegIdString(regid)
95	}
96	return fmt.Sprintf("%v@{%s+%v}", m.Kind(), regstr, m.Offset())
97
98}
99
100// print arm64 machine code as sequence of 4-byte instructions
101func (Arm64) CodeString(code MachineCode) string {
102	const hexdigit string = "0123456789abcdef"
103	bytes := code.Bytes
104	i, j, n := 0, 0, len(bytes)
105	buf := make([]byte, (n+3)/4*9)
106	for ; i+4 <= n; i += 4 {
107		buf[j+0] = hexdigit[bytes[i+3]>>4]
108		buf[j+1] = hexdigit[bytes[i+3]&0xF]
109		buf[j+2] = hexdigit[bytes[i+2]>>4]
110		buf[j+3] = hexdigit[bytes[i+2]&0xF]
111		buf[j+4] = hexdigit[bytes[i+1]>>4]
112		buf[j+5] = hexdigit[bytes[i+1]&0xF]
113		buf[j+6] = hexdigit[bytes[i+0]>>4]
114		buf[j+7] = hexdigit[bytes[i+0]&0xF]
115		buf[j+8] = ' '
116		j += 9
117	}
118	for k := n - 1; k >= i; k-- {
119		buf[j+0] = hexdigit[bytes[k]>>4]
120		buf[j+1] = hexdigit[bytes[k]&0xF]
121		j += 2
122	}
123	return string(buf[:j])
124}
125
126// Prologue used to add the following instruction to generated code,
127// but now it does nothing, because adding ANY code is the user's responsibility:
128//   ldr x29, [sp, #8]
129// equivalent to:
130// asm.Asm(MOV, MakeMem(8, XSP, Uint64), MakeReg(X29, Uint64))
131func (Arm64) Prologue(asm *Asm) *Asm {
132	// return asm.Uint32(0xf94007fd)
133	// equivalent:
134	// return asm.Asm(MOV, MakeMem(8, XSP, Uint64), MakeReg(X29, Uint64))
135	return asm
136}
137
138func (arch Arm64) Epilogue(asm *Asm) *Asm {
139	return arch.Op0(asm, RET)
140}
141
142func (Arm64) Init(asm *Asm, start SaveSlot, end SaveSlot) *Asm {
143	asm.RegIncUse(X28) // pointer to goroutine-local data
144	asm.RegIncUse(X30) // return address register
145	asm.RegIncUse(XZR) // zero register / stack pointer
146	return asm
147}
148