github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/ir/instructions.go (about) 1 package ir 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/arnodel/golua/ops" 8 ) 9 10 // Instruction is the interface that all ir instruction types must implement. 11 type Instruction interface { 12 fmt.Stringer 13 ProcessInstr(InstrProcessor) 14 } 15 16 // SetRegInstruction is the interface that instructions that set the value of a 17 // register must implement. 18 type SetRegInstruction interface { 19 DestReg() Register 20 WithDestReg(Register) Instruction 21 } 22 23 // An InstrProcessor can process any instruction. 24 type InstrProcessor interface { 25 26 // Real instructions 27 ProcessCombineInstr(Combine) 28 ProcessTransformInstr(Transform) 29 ProcessLoadConstInstr(LoadConst) 30 ProcessPushInstr(Push) 31 ProcessJumpInstr(Jump) 32 ProcessJumpIfInstr(JumpIf) 33 ProcessCallInstr(Call) 34 ProcessMkClosureInstr(MkClosure) 35 ProcessMkContInstr(MkCont) 36 ProcessClearRegInstr(ClearReg) 37 ProcessMkTableInstr(MkTable) 38 ProcessLookupInstr(Lookup) 39 ProcessSetIndexInstr(SetIndex) 40 ProcessReceiveInstr(Receive) 41 ProcessReceiveEtcInstr(ReceiveEtc) 42 ProcessEtcLookupInstr(EtcLookup) 43 ProcessFillTableInstr(FillTable) 44 ProcessTruncateCloseStackInstr(TruncateCloseStack) 45 ProcessPushCloseStackInstr(PushCloseStack) 46 ProcessPrepForLoopInstr(PrepForLoop) 47 ProcessAdvForLoopInstr(AdvForLoop) 48 49 // These are hints that a register is needed or no longer needed. 50 ProcessTakeRegisterInstr(TakeRegister) 51 ProcessReleaseRegisterInstr(ReleaseRegister) 52 53 // A label (for jumping to) 54 ProcessDeclareLabelInstr(DeclareLabel) 55 } 56 57 // A Register is an IR register. The number of IR registers is not bounded 58 // (other than the bounds of the underling type). 59 type Register uint 60 61 func (r Register) String() string { 62 return fmt.Sprintf("r%d", r) 63 } 64 65 // A Label is a location in the IR code. 66 type Label uint 67 68 func (l Label) String() string { 69 return fmt.Sprintf("L%d", l) 70 } 71 72 // Combine applies the binary operator Op to Lsrc and Rsrc and stores the result 73 // in Dst. 74 type Combine struct { 75 Op ops.Op // Operator to apply to Lsrc and Rsrc 76 Dst Register // Destination register 77 Lsrc Register // Left operand register 78 Rsrc Register // Right operand register 79 } 80 81 // DestReg returns the destination register of this instruction. 82 func (c Combine) DestReg() Register { 83 return c.Dst 84 } 85 86 // WithDestReg returns the same isntruction with a new destination register. 87 func (c Combine) WithDestReg(r Register) Instruction { 88 c.Dst = r 89 return c 90 } 91 92 // ProcessInstr makes the InstrProcessor process this instruction. 93 func (c Combine) ProcessInstr(p InstrProcessor) { 94 p.ProcessCombineInstr(c) 95 } 96 97 func (c Combine) String() string { 98 return fmt.Sprintf("%s := %s(%s, %s)", c.Dst, c.Op, c.Lsrc, c.Rsrc) 99 } 100 101 // Transform applies a unary operator Op to Src and stores the result in Dst. 102 type Transform struct { 103 Op ops.Op // Operator to apply to Src 104 Dst Register // Destination register 105 Src Register // Operand register 106 } 107 108 // DestReg returns the destination register of this instruction. 109 func (t Transform) DestReg() Register { 110 return t.Dst 111 } 112 113 // WithDestReg returns the same isntruction with a new destination register. 114 func (t Transform) WithDestReg(r Register) Instruction { 115 t.Dst = r 116 return t 117 } 118 119 // ProcessInstr makes the InstrProcessor process this instruction. 120 func (t Transform) ProcessInstr(p InstrProcessor) { 121 p.ProcessTransformInstr(t) 122 } 123 124 func (t Transform) String() string { 125 return fmt.Sprintf("%s := %s(%s)", t.Dst, t.Op, t.Src) 126 } 127 128 // LoadConst loads a constant into a register. 129 type LoadConst struct { 130 Dst Register // Destination register 131 Kidx uint // Index of the constant to load 132 } 133 134 // DestReg returns the destination register of this instruction. 135 func (l LoadConst) DestReg() Register { 136 return l.Dst 137 } 138 139 // WithDestReg returns the same isntruction with a new destination register. 140 func (l LoadConst) WithDestReg(r Register) Instruction { 141 l.Dst = r 142 return l 143 } 144 145 // ProcessInstr makes the InstrProcessor process this instruction. 146 func (l LoadConst) ProcessInstr(p InstrProcessor) { 147 p.ProcessLoadConstInstr(l) 148 } 149 150 func (l LoadConst) String() string { 151 return fmt.Sprintf("%s := k%d", l.Dst, l.Kidx) 152 } 153 154 // Push pushes the contents of a register into a continuation. 155 type Push struct { 156 Cont Register // Destination register (should contain a continuation) 157 Item Register // Register containing item to push 158 Etc bool // True if the Item is an etc value. 159 } 160 161 // ProcessInstr makes the InstrProcessor process this instruction. 162 func (p Push) ProcessInstr(ip InstrProcessor) { 163 ip.ProcessPushInstr(p) 164 } 165 166 func (p Push) String() string { 167 return fmt.Sprintf("push %s to %s", p.Item, p.Cont) 168 } 169 170 // Jump jumps to the givel label. 171 type Jump struct { 172 Label Label 173 } 174 175 func (j Jump) String() string { 176 return fmt.Sprintf("jump %s", j.Label) 177 } 178 179 // ProcessInstr makes the InstrProcessor process this instruction. 180 func (j Jump) ProcessInstr(p InstrProcessor) { 181 p.ProcessJumpInstr(j) 182 } 183 184 // JumpIf jumps to the given label if the boolean value in Cond is different from Not. 185 type JumpIf struct { 186 Cond Register 187 Label Label 188 Not bool 189 } 190 191 // ProcessInstr makes the InstrProcessor process this instruction. 192 func (j JumpIf) ProcessInstr(p InstrProcessor) { 193 p.ProcessJumpIfInstr(j) 194 } 195 196 func (j JumpIf) String() string { 197 return fmt.Sprintf("jump %s if %s is not %t", j.Label, j.Cond, j.Not) 198 } 199 200 // Call moves execution to the given continuation 201 type Call struct { 202 Cont Register 203 Tail bool 204 } 205 206 // ProcessInstr makes the InstrProcessor process this instruction. 207 func (c Call) ProcessInstr(p InstrProcessor) { 208 p.ProcessCallInstr(c) 209 } 210 211 func (c Call) String() string { 212 return fmt.Sprintf("call %s, tail=%t", c.Cont, c.Tail) 213 } 214 215 // MkClosure creates a new closure with the given code and upvalues and puts it in Dst. 216 type MkClosure struct { 217 Dst Register 218 Code uint 219 Upvalues []Register 220 } 221 222 // DestReg returns the destination register of this instruction. 223 func (m MkClosure) DestReg() Register { 224 return m.Dst 225 } 226 227 // WithDestReg returns the same isntruction with a new destination register. 228 func (m MkClosure) WithDestReg(r Register) Instruction { 229 m.Dst = r 230 return m 231 } 232 233 // ProcessInstr makes the InstrProcessor process this instruction. 234 func (m MkClosure) ProcessInstr(p InstrProcessor) { 235 p.ProcessMkClosureInstr(m) 236 } 237 238 func (m MkClosure) String() string { 239 return fmt.Sprintf("%s := mkclos(k%d; %s)", m.Dst, m.Code, joinRegisters(m.Upvalues, ", ")) 240 } 241 242 func joinRegisters(regs []Register, sep string) string { 243 us := []string{} 244 for _, r := range regs { 245 us = append(us, r.String()) 246 } 247 return strings.Join(us, sep) 248 } 249 250 // MkCont creates a new continuation for the given closure and puts it in Dst. 251 type MkCont struct { 252 Dst Register 253 Closure Register 254 Tail bool 255 } 256 257 // DestReg returns the destination register of this instruction. 258 func (m MkCont) DestReg() Register { 259 return m.Dst 260 } 261 262 // WithDestReg returns the same isntruction with a new destination register. 263 func (m MkCont) WithDestReg(r Register) Instruction { 264 m.Dst = r 265 return m 266 } 267 268 // ProcessInstr makes the InstrProcessor process this instruction. 269 func (m MkCont) ProcessInstr(p InstrProcessor) { 270 p.ProcessMkContInstr(m) 271 } 272 273 func (m MkCont) String() string { 274 return fmt.Sprintf("%s := mkcont(%s, tail=%t)", m.Dst, m.Closure, m.Tail) 275 } 276 277 // ClearReg resets the given register to nil (if it contained a cell, this cell 278 // is removed). 279 type ClearReg struct { 280 Dst Register 281 } 282 283 // ProcessInstr makes the InstrProcessor process this instruction. 284 func (i ClearReg) ProcessInstr(p InstrProcessor) { 285 p.ProcessClearRegInstr(i) 286 } 287 288 func (i ClearReg) String() string { 289 return fmt.Sprintf("clrreg(%s)", i.Dst) 290 } 291 292 // MkTable creates a new empty table and puts it i Dst. 293 type MkTable struct { 294 Dst Register 295 } 296 297 // ProcessInstr makes the InstrProcessor process this instruction. 298 func (m MkTable) ProcessInstr(p InstrProcessor) { 299 p.ProcessMkTableInstr(m) 300 } 301 302 func (m MkTable) String() string { 303 return fmt.Sprintf("%s := mktable()", m.Dst) 304 } 305 306 // Lookup finds the value associated with the key Index in Table and puts it in 307 // Dst. 308 type Lookup struct { 309 Dst Register 310 Table Register 311 Index Register 312 } 313 314 // DestReg returns the destination register of this instruction. 315 func (s Lookup) DestReg() Register { 316 return s.Dst 317 } 318 319 // WithDestReg returns the same isntruction with a new destination register. 320 func (s Lookup) WithDestReg(r Register) Instruction { 321 s.Dst = r 322 return s 323 } 324 325 // ProcessInstr makes the InstrProcessor process this instruction. 326 func (s Lookup) ProcessInstr(p InstrProcessor) { 327 p.ProcessLookupInstr(s) 328 } 329 330 func (s Lookup) String() string { 331 return fmt.Sprintf("%s := %s[%s]", s.Dst, s.Table, s.Index) 332 } 333 334 // SetIndex associates Index with Src in the table Table. 335 type SetIndex struct { 336 Table Register 337 Index Register 338 Src Register 339 } 340 341 // ProcessInstr makes the InstrProcessor process this instruction. 342 func (s SetIndex) ProcessInstr(p InstrProcessor) { 343 p.ProcessSetIndexInstr(s) 344 } 345 346 func (s SetIndex) String() string { 347 return fmt.Sprintf("%s[%s] := %s", s.Table, s.Index, s.Src) 348 } 349 350 // Receive will put the result of pushes in the given registers. 351 type Receive struct { 352 Dst []Register 353 } 354 355 // ProcessInstr makes the InstrProcessor process this instruction. 356 func (r Receive) ProcessInstr(p InstrProcessor) { 357 p.ProcessReceiveInstr(r) 358 } 359 360 func (r Receive) String() string { 361 return fmt.Sprintf("recv(%s)", joinRegisters(r.Dst, ", ")) 362 } 363 364 // ReceiveEtc will put the result of pushes into the given registers. Extra 365 // pushes will be accumulated into the Etc register. 366 type ReceiveEtc struct { 367 Dst []Register 368 Etc Register 369 } 370 371 func (r ReceiveEtc) String() string { 372 return fmt.Sprintf("recv(%s, ...%s)", joinRegisters(r.Dst, ", "), r.Etc) 373 } 374 375 // ProcessInstr makes the InstrProcessor process this instruction. 376 func (r ReceiveEtc) ProcessInstr(p InstrProcessor) { 377 p.ProcessReceiveEtcInstr(r) 378 } 379 380 // EtcLookup finds the value at index Idx in the Etc register and puts it in 381 // Dst. 382 type EtcLookup struct { 383 Etc Register 384 Dst Register 385 Idx int 386 } 387 388 // DestReg returns the destination register of this instruction. 389 func (l EtcLookup) DestReg() Register { 390 return l.Dst 391 } 392 393 // WithDestReg returns the same isntruction with a new destination register. 394 func (l EtcLookup) WithDestReg(r Register) Instruction { 395 l.Dst = r 396 return l 397 } 398 399 func (l EtcLookup) String() string { 400 return fmt.Sprintf("%s := %s[%d]", l.Dst, l.Etc, l.Idx) 401 } 402 403 // ProcessInstr makes the InstrProcessor process this instruction. 404 func (l EtcLookup) ProcessInstr(p InstrProcessor) { 405 p.ProcessEtcLookupInstr(l) 406 } 407 408 // FillTable fills Dst (which must contain a table) with the contents of Etc 409 // (which must be an etc value) starting from the given index. 410 type FillTable struct { 411 Etc Register 412 Dst Register 413 Idx int 414 } 415 416 func (f FillTable) String() string { 417 return fmt.Sprintf("fill %s with %s from %d", f.Dst, f.Etc, f.Idx) 418 } 419 420 // ProcessInstr makes the InstrProcessor process this instruction. 421 func (f FillTable) ProcessInstr(p InstrProcessor) { 422 p.ProcessFillTableInstr(f) 423 } 424 425 // TruncateCloseStack truncates the close stack to Height (which should be >= 426 // 0). This instruction was introduced to support to-be-closed variables which 427 // are part of Lua 5.4 428 type TruncateCloseStack struct { 429 Height int 430 } 431 432 func (t TruncateCloseStack) String() string { 433 return fmt.Sprintf("trunc close stack to %d", t.Height) 434 } 435 436 // ProcessInstr makes the InstrProcessor process this instruction. 437 func (t TruncateCloseStack) ProcessInstr(p InstrProcessor) { 438 p.ProcessTruncateCloseStackInstr(t) 439 } 440 441 // PushCloseStack truncates pushes the value Src to the close stack. This 442 // instruction was introduced to support to-be-closed variables which are part 443 // of Lua 5.4 444 type PushCloseStack struct { 445 Src Register 446 } 447 448 func (i PushCloseStack) String() string { 449 return fmt.Sprintf("push %s to close stack", i.Src) 450 } 451 452 // ProcessInstr makes the InstrProcessor process this instruction. 453 func (i PushCloseStack) ProcessInstr(p InstrProcessor) { 454 p.ProcessPushCloseStackInstr(i) 455 } 456 457 // TakeRegister is not a real instruction. It is a hint to the next stage that 458 // the value in this register will need to be used, so not to overwrite it. A 459 // ReleaseRegister for the same register should be emitted some time later. 460 type TakeRegister struct { 461 Reg Register 462 } 463 464 func (t TakeRegister) String() string { 465 return fmt.Sprintf("take %s", t.Reg) 466 } 467 468 // ProcessInstr makes the InstrProcessor process this instruction. 469 func (t TakeRegister) ProcessInstr(p InstrProcessor) { 470 p.ProcessTakeRegisterInstr(t) 471 } 472 473 // ReleaseRegister is not a real instruction. It is a hint to the next stage 474 // that the value in this register no longer needs to be used, so can be 475 // overwritten. It should be preceded by a TakeRegister for the same register. 476 type ReleaseRegister struct { 477 Reg Register 478 } 479 480 func (r ReleaseRegister) String() string { 481 return fmt.Sprintf("release %s", r.Reg) 482 } 483 484 // ProcessInstr makes the InstrProcessor process this instruction. 485 func (r ReleaseRegister) ProcessInstr(p InstrProcessor) { 486 p.ProcessReleaseRegisterInstr(r) 487 } 488 489 // DeclareLabel is not a real IR instruction. It is a placeholder for the 490 // destination location of a jump label. Being an instruction makes it easier 491 // to perform IR code transformations and keep the labels in correct locations. 492 type DeclareLabel struct { 493 Label Label 494 } 495 496 func (l DeclareLabel) String() string { 497 return fmt.Sprintf("L%d:", l.Label) 498 } 499 500 // ProcessInstr makes the InstrProcessor process this instruction. 501 func (l DeclareLabel) ProcessInstr(p InstrProcessor) { 502 p.ProcessDeclareLabelInstr(l) 503 } 504 505 // PrepForLoop prepares a for loop 506 type PrepForLoop struct { 507 Start, Stop, Step Register 508 } 509 510 func (i PrepForLoop) String() string { 511 return fmt.Sprintf("prepfor %s, %s, %s", i.Start, i.Stop, i.Step) 512 } 513 514 func (i PrepForLoop) ProcessInstr(p InstrProcessor) { 515 p.ProcessPrepForLoopInstr(i) 516 } 517 518 // AdvForLoop advances a for loop 519 520 type AdvForLoop struct { 521 Start, Stop, Step Register 522 } 523 524 func (i AdvForLoop) String() string { 525 return fmt.Sprintf("advfor %s, %s, %s", i.Start, i.Stop, i.Step) 526 } 527 528 func (i AdvForLoop) ProcessInstr(p InstrProcessor) { 529 p.ProcessAdvForLoopInstr(i) 530 }