1/*
2 * gomacro - A Go interpreter with Lisp-like macros
3 *
4 * Copyright (C) 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 * comp.go
12 *
13 *  Created on Feb 10, 2019
14 *      Author Massimiliano Ghilardi
15 */
16
17package jit
18
19import (
20	"bytes"
21
22	"github.com/cosmos72/gomacro/jit/asm"
23)
24
25// argument of Comp.Compile()
26type Source []interface{}
27
28type Comp struct {
29	code Code
30	// code[toassemble] is the first AsmCode
31	// not yet assembled
32	toassemble  int
33	nextSoftReg SoftRegId
34	nextTempReg SoftRegId
35	arch        Arch
36	asm.RegIdConfig
37	asm *Asm
38}
39
40func New() *Comp {
41	var c Comp
42	return c.Init()
43}
44
45func NewArchId(archId ArchId) *Comp {
46	var c Comp
47	return c.InitArchId(archId)
48}
49
50func NewArch(arch Arch) *Comp {
51	var c Comp
52	return c.InitArch(arch)
53}
54
55func (c *Comp) Init() *Comp {
56	return c.InitArchId(ARCH_ID)
57}
58
59func (c *Comp) InitArchId(archId ArchId) *Comp {
60	return c.InitArch(Archs[archId])
61}
62
63func (c *Comp) InitArch(arch Arch) *Comp {
64	if arch == nil {
65		errorf("unknown arch")
66	}
67	c.code = nil
68	c.toassemble = 0
69	c.nextSoftReg = 0
70	c.nextTempReg = FirstTempRegId
71	c.arch = arch
72	c.RegIdConfig = arch.RegIdConfig()
73	if c.asm != nil {
74		c.asm.InitArch(arch)
75	}
76	return c
77}
78
79func (c *Comp) Arch() Arch {
80	return c.arch
81}
82
83func (c *Comp) ArchId() ArchId {
84	if c.arch == nil {
85		return asm.NOARCH
86	}
87	return c.arch.Id()
88}
89
90// return symbolic assembly code
91func (c *Comp) Code() Code {
92	return c.code
93}
94
95// call Comp.Assemble() followed by Comp.Asm().Epilogue()
96func (c *Comp) Epilogue() {
97	c.Assemble()
98	c.Asm().Epilogue()
99}
100
101// discard assembly code and machine code
102func (c *Comp) ClearCode() {
103	c.code = nil
104	c.toassemble = 0
105	if c.asm != nil {
106		c.asm.ClearCode()
107	}
108}
109
110// forget all allocated registers
111func (c *Comp) ClearRegs() {
112	c.nextSoftReg = 0
113	c.nextTempReg = 0
114	if c.asm != nil {
115		c.asm.ClearRegs()
116	}
117}
118
119// return assembler
120func (c *Comp) Asm() *Asm {
121	if c.asm == nil {
122		// create asm.Asm on demand
123		c.asm = asm.NewArch(c.arch)
124	}
125	return c.asm
126}
127
128// assemble the code compiled since previous call
129// to Assemble(), and return machine code
130func (c *Comp) Assemble() MachineCode {
131	asm := c.Asm()
132	if len(c.code) > c.toassemble {
133		asm.Assemble(c.code[c.toassemble:]...)
134		c.toassemble = len(c.code)
135	}
136	return asm.Code()
137}
138
139/*
140 * call Assemble(), then set *funcaddr to assembled machine code.
141 *
142 * funcaddr must be a non-nil pointer to function.
143 *
144 * function type MUST match the code created by the programmer,
145 * or BAD things will happen: crash, memory corruption, undefined behaviour...
146 *
147 * Obviously, code created by the programmer must be for the same architecture
148 * the program is currently running on...
149 *
150 * Caller likely needs to call ClearCode() after this function returns
151 */
152func (c *Comp) Func(funcaddr interface{}) {
153	c.Assemble()
154	c.asm.Func(funcaddr)
155}
156
157func checkAssignable(e Expr) {
158	switch e.(type) {
159	case Reg, Mem, SoftReg:
160		break
161	default:
162		errorf("cannot assign to %v", e)
163	}
164}
165
166func (c *Comp) MakeParam(off int32, kind Kind) Mem {
167	return MakeParam(off, kind, c.RegIdConfig)
168}
169
170func (c *Comp) MakeVar(idx int, kind Kind) Mem {
171	mem, err := MakeVar(idx, kind, c.RegIdConfig)
172	if err != nil {
173		panic(err)
174	}
175	return mem
176}
177
178// compile statements and their arguments
179func (c *Comp) Compile(s Source) {
180	for i := 0; i < len(s); i++ {
181		switch inst := s[i].(type) {
182		case Inst1:
183			c.Stmt1(inst, s[i+1].(Expr))
184			i++
185		case Inst1Misc:
186			c.SoftReg(inst, s[i+1].(SoftReg))
187			i++
188		case Inst2:
189			c.Stmt2(inst, s[i+1].(Expr), s[i+2].(Expr))
190			i += 2
191		case Inst3:
192			c.Stmt3(inst, s[i+1].(Expr), s[i+2].(Expr), s[i+3].(Expr))
193			i += 3
194		default:
195			errorf("unknown instruction type %T, expecting Inst1, Inst2, Inst3 or Inst1Misc", inst)
196		}
197	}
198}
199
200// pretty-print Source
201func (s Source) String() string {
202	var buf bytes.Buffer
203	for i := 0; i < len(s); i++ {
204		if i != 0 {
205			buf.WriteByte(' ')
206		}
207		switch inst := s[i].(type) {
208		case Inst1:
209			buf.WriteString(NewStmt1(inst, s[i+1].(Expr)).String())
210			i++
211		case Inst1Misc:
212			buf.WriteString(inst.String())
213			buf.WriteByte(' ')
214			buf.WriteString(s[i+1].(SoftReg).String())
215			buf.WriteByte(';')
216			i++
217		case Inst2:
218			buf.WriteString(NewStmt2(inst, s[i+1].(Expr), s[i+2].(Expr)).String())
219			i += 2
220		case Inst3:
221			buf.WriteString(NewStmt3(inst, s[i+1].(Expr), s[i+2].(Expr), s[i+3].(Expr)).String())
222			i += 3
223		default:
224			errorf("unknown instruction type %T, expecting Inst1, Inst2 or Inst3", inst)
225		}
226	}
227	return buf.String()
228}
229