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