This commit is contained in:
Kartik Agaram 2020-03-21 17:25:52 -07:00
parent df609237c1
commit fa80585024
1 changed files with 143 additions and 196 deletions

View File

@ -1,243 +1,190 @@
## Mu's instructions and their table-driven translation ## Mu's instructions and their table-driven translation
Mu is a statement-oriented language. Blocks consist of flat lists of instructions. Mu is a statement-oriented language. Blocks consist of flat lists of instructions.
The chart at the bottom of this page shows all the instruction forms supported The following chart shows all the instruction forms supported by Mu, along
by Mu, one to a line. Each line shows an example of the instruction on the with the instruction they're translated to. Variables of the form "var/reg"
left. Past the first column, everything inside the {..} is a summary of the live in a register, and other variables are assumed to live on the stack at
data structure the Mu compiler uses (`Primitives` in apps/mu.subx) to translate some 'stack-offset' from ebp.
it.
The syntax of the data structure is intended to be similar to C++'s aggregate var/eax <- increment => "40/increment-eax"
initializers (https://en.cppreference.com/w/cpp/language/aggregate_initialization) var/ecx <- increment => "41/increment-ecx"
However, there are differences: var/edx <- increment => "42/increment-edx"
- We use adjacency for string concatenation. var/ebx <- increment => "43/increment-ebx"
- We use [] for array literals. var/esi <- increment => "46/increment-esi"
- The objects inside [] are not fully described. They include various var/edi <- increment => "47/increment-edi"
metadata about the variable in the instruction. For our purposes, assume increment var => "ff 0/subop/increment *(ebp+" var.stack-offset ")"
that variables on the stack have a stack offset computed for them, and increment *var/reg => "ff 0/subop/increment *" reg
register variables evaluate to their register.
- registers may be specified by name: /eax /ecx /edx /ebx /esi /edi
- registers may be specified as a wildcard: /reg
- integer literals are always called 'n'
- any other variable names that don't specify a register are assumed to be on the stack
There are no checks for types yet, because Mu programs only have `int` types so far. var/eax <- decrement => "48/decrement-eax"
var/ecx <- decrement => "49/decrement-ecx"
var/edx <- decrement => "4a/decrement-edx"
var/ebx <- decrement => "4b/decrement-ebx"
var/esi <- decrement => "4e/decrement-esi"
var/edi <- decrement => "4f/decrement-edi"
decrement var => "ff 1/subop/decrement *(ebp+" var.stack-offset ")"
decrement *var/reg => "ff 1/subop/decrement *" reg
Example 1 (use the widest screen you can for this page): var/reg <- add var2/reg2 => "01/add-to %" reg " " reg2 "/r32"
-- instruction form -- | -------------------------- data structure ---------------------------- var/reg <- add var2 => "03/add *(ebp+" var2.stack-offset ") " reg "/r32"
|<------------- pattern matching ---------->|<--- code generation -------------------> var/reg <- add *var2/reg2 => "03/add *" reg2 " " reg "/r32"
var/reg <- add var2/reg {.name="add", .inouts=[reg], .outputs=[reg], .subx-name="01/add-to", .rm32=outputs[0], .r32=inouts[0]} add-to var1, var2/reg => "01/add-to *(ebp+" var1.stack-offset ") " reg "/r32"
var/eax <- add n => "05/add-to-eax " n "/imm32"
var/reg <- add n => "81 0/subop/add %" reg " " n "/imm32"
add-to var, n => "81 0/subop/add *(ebp+" var.stack-offset ") " n "/imm32"
add-to *var/reg, n => "81 0/subop/add *" reg " " n "/imm32"
Read this as: var/reg <- subtract var2/reg2 => "29/subtract-from %" reg " " reg2 "/r32"
if an instruction's name is "add" var/reg <- subtract var2 => "2b/subtract *(ebp+" var2.stack-offset ") " reg "/r32"
and it has one inout that's in a register var/reg <- subtract *var2/reg2 => "2b/subtract *" reg2 " " reg1 "/r32"
and it has one output that's in a register, subtract-from var1, var2/reg2 => "29/subtract-from *(ebp+" var1.stack-offset ") " reg2 "/r32"
then emit the following on a single line var/eax <- subtract n => "2d/subtract-from-eax " n "/imm32"
"01/add-to" (the opcode or subx-name) var/reg <- subtract n => "81 5/subop/subtract %" reg " " n "/imm32"
"%{reg}", interpolating the output's register subtract-from var, n => "81 5/subop/subtract *(ebp+" var.stack-offset ") " n "/imm32"
"{reg}/r32", interpolating the inout's register code. subtract-from *var/reg, n => "81 5/subop/subtract *" reg " " n "/imm32"
Example 2: var/reg <- and var2/reg2 => "21/and-with %" reg " " reg2 "/r32"
-- instruction form -- | -------------------------- data structure ---------------------------- var/reg <- and var2 => "23/and *(ebp+" var2.stack-offset " " reg "/r32"
|<------- pattern matching ------>|<--- code generation -------------------> var/reg <- and *var2/reg2 => "23/and *" reg2 " " reg "/r32"
add-to var, n {.name="add-to", .inouts=[var, n], .subx-name="81 0/subop/add", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]} and-with var1, var2/reg => "21/and-with *(ebp+" var1.stack-offset ") " reg "/r32"
var/eax <- and n => "25/and-with-eax " n "/imm32"
var/reg <- and n => "81 4/subop/and %" reg " " n "/imm32"
and-with var, n => "81 4/subop/and *(ebp+" var.stack-offset ") " n "/imm32"
and-with *var/reg, n => "81 4/subop/and *" reg " " n "/imm32"
Read this as: var/reg <- or var2/reg2 => "09/or-with %" reg " " reg2 "/r32"
if an instruction's name is "add-to" var/reg <- or var2 => "0b/or *(ebp+" var2.stack-offset ") " reg "/r32"
and it has two inouts var/reg <- or *var2/reg2 => "0b/or *" reg2 " " reg "/r32"
the first on the stack or-with var1, var2/reg2 => "09/or-with *(ebp+" var1.stack-offset " " reg2 "/r32"
and the second a literal, var/eax <- or n => "0d/or-with-eax " n "/imm32"
then emit the following on a single line var/reg <- or n => "81 1/subop/or %" reg " " n "/imm32"
"81 0/subop/add" (the opcode or subx-name) or-with var, n => "81 1/subop/or *(ebp+" var.stack-offset ") " n "/imm32"
"*(ebp+{stack})", interpolating the first inout's stack offset or-with *var/reg, n => "81 1/subop/or *" reg " " n "/imm32"
"{n}/imm32", interpolating the second inout's contents
Ok, here's the complete chart. var/reg <- xor var2/reg2 => "31/xor-with %" reg " " reg2 "/r32"
var/reg <- xor var2 => "33/xor *(ebp+" var2.stack-offset ") " reg "/r32"
var/reg <- xor *var2/reg2 => "33/xor *" reg2 " " reg "/r32"
xor-with var1, var2/reg => "31/xor-with *(ebp+" var1.stack-offset ") " reg "/r32"
var/eax <- xor n => "35/xor-with-eax " n "/imm32"
var/reg <- xor n => "81 6/subop/xor %" reg " " n "/imm32"
xor-with var, n => "81 6/subop/xor *(ebp+" var.stack-offset ") " n "/imm32"
xor-with *var/reg, n => "81 6/subop/xor *" reg " " n "/imm32"
-- instruction form -- | -------------------------- data structure ---------------------------- var/eax <- copy n => "b8/copy-to-eax " n "/imm32"
|<------------------- pattern matching ------------------->|<---- code generation -------------------> var/ecx <- copy n => "b9/copy-to-ecx " n "/imm32"
var/eax <- increment {.name="increment", .outputs=[eax], .subx-name="40/increment-eax"} var/edx <- copy n => "ba/copy-to-edx " n "/imm32"
var/ecx <- increment {.name="increment", .outputs=[ecx], .subx-name="41/increment-ecx"} var/ebx <- copy n => "bb/copy-to-ebx " n "/imm32"
var/edx <- increment {.name="increment", .outputs=[edx], .subx-name="42/increment-edx"} var/esi <- copy n => "be/copy-to-esi " n "/imm32"
var/ebx <- increment {.name="increment", .outputs=[ebx], .subx-name="43/increment-ebx"} var/edi <- copy n => "bf/copy-to-edi " n "/imm32"
var/esi <- increment {.name="increment", .outputs=[esi], .subx-name="46/increment-esi"} var/reg <- copy var2/reg2 => "89/<- %" reg " " reg2 "/r32"
var/edi <- increment {.name="increment", .outputs=[edi], .subx-name="47/increment-edi"} copy-to var1, var2/reg => "89/<- *(ebp+" var1.stack-offset ") " reg "/r32"
increment var {.name="increment", .inouts=[var], .subx-name="ff 0/subop/increment", .rm32="*(ebp+" inouts[0].stack-offset ")"} var/reg <- copy var2 => "8b/-> *(ebp+" var2.stack-offset ") " reg "/r32"
increment *var/reg {.name="increment", .inouts=[reg], .subx-name="ff 0/subop/increment", .rm32="*" inouts[0]} var/reg <- copy *var2/reg2 => "8b/-> *" reg2 " " reg "/r32"
var/reg <- copy n => "c7 0/subop/copy %" reg " " n "/imm32"
copy-to var, n => "c7 0/subop/copy *(ebp+" var.stack-offset ") " n "/imm32"
copy-to *var/reg, n => "c7 0/subop/copy *" reg " " n "/imm32"
var/eax <- decrement {.name="decrement", .outputs=[eax], .subx-name="48/decrement-eax"} compare var1, var2/reg2 => "39/compare *(ebp+" var1.stack-offset ") " reg2 "/r32"
var/ecx <- decrement {.name="decrement", .outputs=[ecx], .subx-name="49/decrement-ecx"} compare *var1/reg1, var2/reg2 => "39/compare *" reg1 " " reg2 "/r32"
var/edx <- decrement {.name="decrement", .outputs=[edx], .subx-name="4a/decrement-edx"} compare var1/reg1, var2 => "3b/compare<- *(ebp+" var2.stack-offset ") " reg1 "/r32"
var/ebx <- decrement {.name="decrement", .outputs=[ebx], .subx-name="4b/decrement-ebx"} compare var/reg, *var2/reg2 => "3b/compare<- *" reg " " n "/imm32"
var/esi <- decrement {.name="decrement", .outputs=[esi], .subx-name="4e/decrement-esi"} compare var/eax, n => "3d/compare-eax-with " n "/imm32"
var/edi <- decrement {.name="decrement", .outputs=[edi], .subx-name="4f/decrement-edi"} compare var, n => "81 7/subop/compare *(ebp+" var.stack-offset ") " n "/imm32"
decrement var {.name="decrement", .inouts=[var], .subx-name="ff 1/subop/decrement", .rm32="*(ebp+" inouts[0].stack-offset ")"} compare *var/reg, n => "81 7/subop/compare *" reg " " n "/imm32"
decrement *var/reg {.name="decrement", .inouts=[reg], .subx-name="ff 1/subop/decrement", .rm32="*" inouts[0]}
var1/reg1 <- add var2/reg2 {.name="add", .inouts=[reg2], .outputs=[reg1], .subx-name="01/add-to", .rm32=outputs[0], .r32=inouts[0]} var/reg <- multiply var2 => "0f af/multiply *(ebp+" var2.stack-offset ") " reg "/r32"
var/reg <- add var2 {.name="add", .inouts=[var2], .outputs=[reg], .subx-name="03/add", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]} var/reg <- multiply *var2/reg2 => "0f af/multiply *" reg2 " " reg "/r32"
var/reg <- add *var2/reg2 {.name="add", .inouts=[reg2], .outputs=[reg], .subx-name="03/add", .rm32="*" inouts[0], .r32=outputs[0]}
add-to var1, var2/reg {.name="add-to", .inouts=[var1, var2], .subx-name="01/add-to", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
var/eax <- add n {.name="add", .inouts=[n], .outputs=[eax], .subx-name="05/add-to-eax", .imm32=inouts[0]}
var/reg <- add n {.name="add", .inouts=[n], .outputs=[reg], .subx-name="81 0/subop/add", .rm32=outputs[0], .imm32=inouts[0]}
add-to var, n {.name="add-to", .inouts=[var, n], .subx-name="81 0/subop/add", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
add-to *var/reg, n {.name="add-to", .inouts=[reg, n], .subx-name="81 0/subop/add", .rm32="*" inouts[0], .imm32=inouts[1]}
var1/reg1 <- sub var2/reg2 {.name="sub", .inouts=[reg2], .outputs=[reg1], .subx-name="29/subtract-from", .rm32=outputs[0], .r32=inouts[0]} break => "e9/jump break/disp32"
var/reg <- sub var2 {.name="sub", .inouts=[var2], .outputs=[reg], .subx-name="2b/subtract", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]} break label => "e9/jump " label ":break/disp32"
var/reg <- sub *var2/reg2 {.name="sub", .inouts=[reg2], .outputs=[reg], .subx-name="2b/subtract", .rm32="*" inouts[0], .r32=outputs[0]} loop => "e9/jump loop/disp32"
sub-from var1, var2/reg {.name="sub-from", .inouts=[var1, var2], .subx-name="29/subtract-from", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]} loop label => "e9/jump " label ":loop/disp32"
var/eax <- sub n {.name="sub", .inouts=[n], .outputs=[eax], .subx-name="2d/subtract-from-eax", .imm32=inouts[0]}
var/reg <- sub n {.name="sub", .inouts=[n], .outputs=[reg], .subx-name="81 5/subop/subtract", .rm32=outputs[0], .imm32=inouts[0]}
sub-from var, n {.name="sub-from", .inouts=[var, n], .subx-name="81 5/subop/subtract", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
sub-from *var/reg, n {.name="sub-from", .inouts=[reg, n], .subx-name="81 5/subop/subtract", .rm32="*" inouts[0], .imm32=inouts[1]}
var1/reg1 <- and var2/reg2 {.name="and", .inouts=[reg2], .outputs=[reg1], .subx-name="21/and-with", .rm32=outputs[0], .r32=inouts[0]} break-if-= => "0f 84/jump-if-= break/disp32"
var/reg <- and var2 {.name="and", .inouts=[var2], .outputs=[reg], .subx-name="23/and", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]} break-if-= label => "0f 84/jump-if-= " label ":break/disp32"
var/reg <- and *var2/reg2 {.name="and", .inouts=[reg2], .outputs=[reg], .subx-name="23/and", .rm32="*" inouts[0], .r32=outputs[0]} loop-if-= => "0f 84/jump-if-= loop/disp32"
and-with var1, var2/reg {.name="and-with", .inouts=[var1, reg], .subx-name="21/and-with", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]} loop-if-= label => "0f 84/jump-if-= " label ":loop/disp32"
var/eax <- and n {.name="and", .inouts=[n], .outputs=[eax], .subx-name="25/and-with-eax", .imm32=inouts[0]}
var/reg <- and n {.name="and", .inouts=[n], .outputs=[reg], .subx-name="81 4/subop/and", .rm32=outputs[0], .imm32=inouts[0]}
and-with var, n {.name="and-with", .inouts=[var, n], .subx-name="81 4/subop/and", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
and-with *var/reg, n {.name="and-with", .inouts=[reg, n], .subx-name="81 4/subop/and", .rm32="*" inouts[0], .imm32=inouts[1]}
var1/reg1 <- or var2/reg2 {.name="or", .inouts=[reg2], .outputs=[reg1], .subx-name="09/or-with", .rm32=outputs[0], .r32=inouts[0]} break-if-!= => "0f 85/jump-if-!= break/disp32"
var/reg <- or var2 {.name="or", .inouts=[var2], .outputs=[reg], .subx-name="0b/or", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]} break-if-!= label => "0f 85/jump-if-!= " label ":break/disp32"
var/reg <- or *var2/reg2 {.name="or", .inouts=[reg2], .outputs=[reg], .subx-name="0b/or", .rm32="*" inouts[0], .r32=outputs[0]} loop-if-!= => "0f 85/jump-if-!= loop/disp32"
or-with var1, var2/reg {.name="or-with", .inouts=[var1, reg], .subx-name="09/or-with", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]} loop-if-!= label => "0f 85/jump-if-!= " label ":loop/disp32"
var/eax <- or n {.name="or", .inouts=[n], .outputs=[eax], .subx-name="0d/or-with-eax", .imm32=inouts[0]}
var/reg <- or n {.name="or", .inouts=[n], .outputs=[reg], .subx-name="81 1/subop/or", .rm32=outputs[0], .imm32=inouts[0]}
or-with var, n {.name="or-with", .inouts=[var, n], .subx-name="81 1/subop/or", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
or-with *var/reg, n {.name="or-with", .inouts=[reg, n], .subx-name="81 1/subop/or", .rm32="*" inouts[0], .imm32=inouts[1]}
var1/reg1 <- xor var2/reg2 {.name="xor", .inouts=[reg2], .outputs=[reg1], .subx-name="31/xor-with", .rm32=outputs[0], .r32=inouts[0]} break-if-< => "0f 8c/jump-if-< break/disp32"
var/reg <- xor var2 {.name="xor", .inouts=[var2], .outputs=[reg], .subx-name="33/xor", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]} break-if-< label => "0f 8c/jump-if-< " label ":break/disp32"
var/reg <- xor *var2/reg2 {.name="xor", .inouts=[reg2], .outputs=[reg], .subx-name="33/xor", .rm32="*" inouts[0], .r32=outputs[0]} loop-if-< => "0f 8c/jump-if-< loop/disp32"
xor-with var1, var2/reg {.name="xor-with", .inouts=[var1, reg], .subx-name="31/xor-with", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]} loop-if-< label => "0f 8c/jump-if-< " label ":loop/disp32"
var/eax <- xor n {.name="xor", .inouts=[n], .outputs=[eax], .subx-name="35/xor-with-eax", .imm32=inouts[0]}
var/reg <- xor n {.name="xor", .inouts=[n], .outputs=[reg], .subx-name="81 6/subop/xor", .rm32=outputs[0], .imm32=inouts[0]}
xor-with var, n {.name="xor-with", .inouts=[var, n], .subx-name="81 6/subop/xor", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
xor-with *var/reg, n {.name="xor-with", .inouts=[reg, n], .subx-name="81 6/subop/xor", .rm32="*" inouts[0], .imm32=inouts[1]}
var/eax <- copy n {.name="copy", .inouts=[n], .outputs=[eax], .subx-name="b8/copy-to-eax", .imm32=inouts[0]} break-if-> => "0f 8f/jump-if-> break/disp32"
var/ecx <- copy n {.name="copy", .inouts=[n], .outputs=[ecx], .subx-name="b9/copy-to-ecx", .imm32=inouts[0]} break-if-> label => "0f 8f/jump-if-> " label ":break/disp32"
var/edx <- copy n {.name="copy", .inouts=[n], .outputs=[edx], .subx-name="ba/copy-to-edx", .imm32=inouts[0]} loop-if-> => "0f 8f/jump-if-> loop/disp32"
var/ebx <- copy n {.name="copy", .inouts=[n], .outputs=[ebx], .subx-name="bb/copy-to-ebx", .imm32=inouts[0]} loop-if-> label => "0f 8f/jump-if-> " label ":loop/disp32"
var/esi <- copy n {.name="copy", .inouts=[n], .outputs=[esi], .subx-name="be/copy-to-esi", .imm32=inouts[0]}
var/edi <- copy n {.name="copy", .inouts=[n], .outputs=[edi], .subx-name="bf/copy-to-edi", .imm32=inouts[0]}
var1/reg1 <- copy var2/reg2 {.name="copy", .inouts=[reg2], .outputs=[reg1], .subx-name="89/<-", .rm32=outputs[0], .r32=inouts[0]}
copy-to var1, var2/reg {.name="copy-to", .inouts=[var1, var2], .subx-name="89/<-", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]}
var/reg <- copy var2 {.name="copy", .inouts=[var2], .outputs=[reg], .subx-name="8b/->", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]}
var1/reg <- copy *var2/reg2 {.name="copy", .inouts=[reg2], .outputs=[reg], .subx-name="8b/->", .rm32="*" inouts[0], .r32=outputs[0]}
var/reg <- copy n {.name="copy", .inouts=[n], .outputs=[reg], .subx-name="c7 0/subop/copy", .rm32=outputs[0], .imm32=inouts[0]}
copy-to var, n {.name="copy-to", .inouts=[var, n], .subx-name="c7 0/subop/copy", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
copy-to *var/reg, n {.name="copy-to", .inouts=[reg, n], .subx-name="c7 0/subop/copy", .rm32="*" inouts[0], .imm32=inouts[1]}
compare var1, var2/reg {.name="compare", .inouts=[var1, reg], .subx-name="39/compare", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=inouts[1]} break-if-<= => "0f 8e/jump-if-<= break/disp32"
compare *var1/reg1, var/reg {.name="compare", .inouts=[reg1, reg], .subx-name="39/compare", .rm32="*" inouts[0], .r32=inouts[1]} break-if-<= label => "0f 8e/jump-if-<= " label ":break/disp32"
compare var1/reg, var2 {.name="compare", .inouts=[reg, var2], .subx-name="3b/compare<-", .rm32="*(ebp+" inouts[1].stack-offset ")", .r32=inouts[0]} loop-if-<= => "0f 8e/jump-if-<= loop/disp32"
compare var/reg, *var2/reg2 {.name="compare", .inouts=[reg, reg2], .subx-name="3b/compare<-", .rm32="*" inouts[1], .r32=inouts[0]} loop-if-<= label => "0f 8e/jump-if-<= " label ":loop/disp32"
compare var/eax, n {.name="compare", .inouts=[eax, n], .subx-name="3d/compare-eax-with", .imm32=inouts[1]}
compare var, n {.name="compare", .inouts=[var, n], .subx-name="81 7/subop/compare", .rm32="*(ebp+" inouts[0].stack-offset ")", .imm32=inouts[1]}
compare *var/reg, n {.name="compare", .inouts=[reg, n], .subx-name="81 7/subop/compare", .rm32="*" inouts[0], .imm32=inouts[1]}
var/reg <- multiply var2 {.name="multiply", .inouts=[var2], .outputs=[reg], .subx-name="0f af/multiply", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]} break-if->= => "0f 8d/jump-if->= break/disp32"
var/reg <- multiply *var2/reg2 { .name="multiply", .inouts=[reg2], .outputs=[reg], .subx-name="0f af/multiply", .rm32="*" inouts[0], .r32=outputs[0]} break-if->= label => "0f 8d/jump-if->= " label ":break/disp32"
loop-if->= => "0f 8d/jump-if->= loop/disp32"
loop-if->= label => "0f 8d/jump-if->= " label ":loop/disp32"
Jumps have a slightly simpler format. Most of the time they take no inouts or break-if-addr< => "0f 82/jump-if-addr< break/disp32"
outputs. Occasionally you give them a label for a containing block to jump to break-if-addr< label => "0f 82/jump-if-addr< " label ":break/disp32"
the start or end of. (Conditional branches read different combinations of CPU loop-if-addr< => "0f 82/jump-if-addr< loop/disp32"
flags.) loop-if-addr< label => "0f 82/jump-if-addr< " label ":loop/disp32"
break {.name="break", .subx-name="e9/jump break/disp32"} break-if-addr> => "0f 87/jump-if-addr> break/disp32"
break label {.name="break", .inouts=[label], .subx-name="e9/jump", .disp32=inouts[0] ":break"} break-if-addr> label => "0f 87/jump-if-addr> " label ":break/disp32"
loop-if-addr> => "0f 87/jump-if-addr> loop/disp32"
loop-if-addr> label => "0f 87/jump-if-addr> " label ":loop/disp32"
break-if-= {.name="break-if-=", .subx-name="0f 84/jump-if-= break/disp32"} break-if-addr<= => "0f 86/jump-if-addr<= break/disp32"
break-if-= label {.name="break-if-=", .inouts=[label], .subx-name="0f 84/jump-if-=", .disp32=inouts[0] ":break"} break-if-addr<= label => "0f 86/jump-if-addr<= " label ":break/disp32"
break-if-!= {.name="break-if-!=", .subx-name="0f 85/jump-if-!= break/disp32"} loop-if-addr<= => "0f 86/jump-if-addr<= loop/disp32"
break-if-!= label {.name="break-if-!=", .inouts=[label], .subx-name="0f 85/jump-if-!=", .disp32=inouts[0] ":break"} loop-if-addr<= label => "0f 86/jump-if-addr<= " label ":loop/disp32"
break-if-< {.name="break-if-<", .subx-name="0f 8c/jump-if-< break/disp32"} break-if-addr>= => "0f 83/jump-if-addr>= break/disp32"
break-if-< label {.name="break-if-<", .inouts=[label], .subx-name="0f 8c/jump-if-<", .disp32=inouts[0] ":break"} break-if-addr>= label => "0f 83/jump-if-addr>= " label ":break/disp32"
break-if-> {.name="break-if->", .subx-name="0f 8f/jump-if-> break/disp32"} loop-if-addr>= => "0f 83/jump-if-addr>= loop/disp32"
break-if-> label {.name="break-if->", .inouts=[label], .subx-name="0f 8f/jump-if->", .disp32=inouts[0] ":break"} loop-if-addr>= label => "0f 83/jump-if-addr>= " label ":loop/disp32"
break-if-<= {.name="break-if-<=", .subx-name="0f 8e/jump-if-<= break/disp32"}
break-if-<= label {.name="break-if-<=", .inouts=[label], .subx-name="0f 8e/jump-if-<=", .disp32=inouts[0] ":break"}
break-if->= {.name="break-if->=", .subx-name="0f 8d/jump-if->= break/disp32"}
break-if->= label {.name="break-if->=", .inouts=[label], .subx-name="0f 8d/jump-if->=", .disp32=inouts[0] ":break"}
break-if-addr< {.name="break-if-addr<", .subx-name="0f 82/jump-if-addr< break/disp32"} In the following instructions types are provided for clarity even if they must
break-if-addr< label {.name="break-if-addr<", .inouts=[label], .subx-name="0f 82/jump-if-addr<", .disp32=inouts[0] ":break"} be provided in an earlier 'var' declaration.
break-if-addr> {.name="break-if-addr>", .subx-name="0f 87/jump-if-addr> break/disp32"}
break-if-addr> label {.name="break-if-addr>", .inouts=[label], .subx-name="0f 87/jump-if-addr>", .disp32=inouts[0] ":break"}
break-if-addr<= {.name="break-if-addr<=", .subx-name="0f 86/jump-if-addr<= break/disp32"}
break-if-addr<= label {.name="break-if-addr<=", .inouts=[label], .subx-name="0f 86/jump-if-addr<=", .disp32=inouts[0] ":break"}
break-if-addr>= {.name="break-if-addr>=", .subx-name="0f 83/jump-if-addr>= break/disp32"}
break-if-addr>= label {.name="break-if-addr>=", .inouts=[label], .subx-name="0f 83/jump-if-addr>=", .disp32=inouts[0] ":break"}
We repeat all the 'break' variants almost identically for 'loop' instructions.
This works because the compiler inserts ':loop' labels at the start of such
named blocks, and ':break' labels at the end.
loop {.name="loop", .subx-name="e9/jump loop/disp32"}
loop label {.name="loop", .inouts=[label], .subx-name="e9/jump", .disp32=inouts[0] ":loop"}
loop-if-= {.name="loop-if-=", .subx-name="0f 84/jump-if-= loop/disp32"}
loop-if-= label {.name="loop-if-=", .inouts=[label], .subx-name="0f 84/jump-if-=", .disp32=inouts[0] ":loop"}
loop-if-!= {.name="loop-if-!=", .subx-name="0f 85/jump-if-!= loop/disp32"}
loop-if-!= label {.name="loop-if-!=", .inouts=[label], .subx-name="0f 85/jump-if-!=", .disp32=inouts[0] ":loop"}
loop-if-< {.name="loop-if-<", .subx-name="0f 8c/jump-if-< loop/disp32"}
loop-if-< label {.name="loop-if-<", .inouts=[label], .subx-name="0f 8c/jump-if-<", .disp32=inouts[0] ":loop"}
loop-if-> {.name="loop-if->", .subx-name="0f 8f/jump-if-> loop/disp32"}
loop-if-> label {.name="loop-if->", .inouts=[label], .subx-name="0f 8f/jump-if->", .disp32=inouts[0] ":loop"}
loop-if-<= {.name="loop-if-<=", .subx-name="0f 8e/jump-if-<= loop/disp32"}
loop-if-<= label {.name="loop-if-<=", .inouts=[label], .subx-name="0f 8e/jump-if-<=", .disp32=inouts[0] ":loop"}
loop-if->= {.name="loop-if->=", .subx-name="0f 8d/jump-if->= loop/disp32"}
loop-if->= label {.name="loop-if->=", .inouts=[label], .subx-name="0f 8d/jump-if->=", .disp32=inouts[0] ":loop"}
loop-if-addr< {.name="loop-if-addr<", .subx-name="0f 82/jump-if-addr< loop/disp32"}
loop-if-addr< label {.name="loop-if-addr<", .inouts=[label], .subx-name="0f 82/jump-if-addr<", .disp32=inouts[0] ":loop"}
loop-if-addr> {.name="loop-if-addr>", .subx-name="0f 87/jump-if-addr> loop/disp32"}
loop-if-addr> label {.name="loop-if-addr>", .inouts=[label], .subx-name="0f 87/jump-if-addr>", .disp32=inouts[0] ":loop"}
loop-if-addr<= {.name="loop-if-addr<=", .subx-name="0f 86/jump-if-addr<= loop/disp32"}
loop-if-addr<= label {.name="loop-if-addr<=", .inouts=[label], .subx-name="0f 86/jump-if-addr<=", .disp32=inouts[0] ":loop"}
loop-if-addr>= {.name="loop-if-addr>=", .subx-name="0f 83/jump-if-addr>= loop/disp32"}
loop-if-addr>= label {.name="loop-if-addr>=", .inouts=[label], .subx-name="0f 83/jump-if-addr>=", .disp32=inouts[0] ":loop"}
Address operations Address operations
var/reg: (addr T) <- address var: T var/reg: (addr T) <- address var: T
{.name="address", .inouts=[var], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(ebp+" inouts[0].stack-offset ")", .r32=outputs[0]} => "8d/copy-address *(ebp+" var.stack-offset ") " reg "/r32"
Array operations Array operations
var/reg <- length arr/reg2: (addr array T) var/reg <- length arr/reg2: (addr array T)
{.name="length", .inouts=[reg2], .outputs=[reg1], .subx-name="8b/copy-from", .rm32="*" inouts[0], .r32=outputs[0]} => "8b/-> *" reg2 " " reg "/r32"
var/reg <- index arr/rega: (addr array T), idx/regi: int var/reg <- index arr/rega: (addr array T), idx/regi: int # if size(T) is 4 or 8
{.name="index", .inouts=[rega, regi], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(" inouts[0] "+" inouts[1] "<<" log2(sizeof(T)) "+4)", .r32=outputs[0]} => "8d/copy-address *(" rega "+" regi "<<" log2(size(T)) "+4) " reg "/r32"
var/reg <- index arr: (array T sz), idx/regi: int var/reg <- index arr: (array T sz), idx/regi: int
{.name="index", .inouts=[arr, regi], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(ebp+" inouts[1] "<<" log2(sizeof(T)) " + " inouts[0].stack-offset+4 ")", .r32=outputs[0]} => "8d/copy-address *(ebp+" regi "<<" log2(size(T)) "+" (arr.stack-offset + 4) ") " reg "/r32"
var/reg <- index arr/rega: (addr array T), n var/reg <- index arr/rega: (addr array T), n
{.name="index", .inouts=[rega, n], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(" inouts[0] "+" inouts[1]*size(T) ")", .r32=outputs[0]} => "8d/copy-address *(" rega "+" (n*size(T)+4) ") " reg "/r32"
var/reg <- index arr: (array T sz), n var/reg <- index arr: (array T sz), n
{.name="index", .inouts=[arr, n], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(ebp+" inouts[0].stack-offset+4 + inouts[1]*size(T) ")", .r32=outputs[0]} => "8d/copy-address *(ebp+" (arr.stack-offset+4+n*size(T)) ") " reg "/r32"
var/reg: (offset T) <- compute-offset arr: (addr array T), idx/regi: int # arr can be in reg or mem var/reg: (offset T) <- compute-offset arr: (addr array T), idx/regi: int # arr can be in reg or mem
{.name="compute-offset", .inouts=[arr, regi], .outputs=[reg], .subx-name="69/multiply", .rm32=inouts[1], .r32=outputs[0], .imm32=sizeof(T)} => "69/multiply %" regi " " size(T) "/imm32 " reg "/r32"
var/reg: (offset T) <- compute-offset arr: (addr array T), idx: int # arr can be in reg or mem var/reg: (offset T) <- compute-offset arr: (addr array T), idx: int # arr can be in reg or mem
{.name="compute-offset", .inouts=[arr, regi], .outputs=[reg], .subx-name="69/multiply", .rm32="*(ebp+" inouts[1].stack-offset ")", .r32=outputs[0], .imm32=sizeof(T)} => "69/multiply *(ebp+" idx.stack-offset ") " size(T) "/imm32 " reg "/r32"
var/reg <- index arr/rega: (addr array T), o/rego: offset var/reg <- index arr/rega: (addr array T), o/rego: offset
{.name="index", .inouts=[rega, rego], .outputs=[reg], .subx-name="8d/copy-address", .rm32="*(" inouts[0] "+" inouts[1] "+" "4)", .r32=outputs[0]} => "8d/copy-address *(" rega "+" rego "+4) " reg "/r32"
User-defined types User-defined types
If a record (product) type T was defined to have elements a, b, c, ... of If a record (product) type T was defined to have elements a, b, c, ... of
types T_a, T_b, T_c, ..., then accessing one of those elements f of type T_f: types T_a, T_b, T_c, ..., then accessing one of those elements f of type T_f:
var/reg: (addr T_f) <- get var2/reg2: (addr F), f: field name => 8d/copy-addr *(reg2+offset(f)) var/reg: (addr T_f) <- get var2/reg2: (addr F), f
{.name="get", .inouts=[reg2, f], .subx-name="8d/copy-address", .rm32="*(" inouts[0] "+" offset(f) ")", .r32=outputs[0]} => "8d/copy-address *(" reg2 "+" offset(f) ") " reg "/r32"
var/reg: (addr T_f) <- get var2: (addr F), f: field name var/reg: (addr T_f) <- get var2: (addr F), f
{.name="get", .inouts=[reg2, f], .subx-name="8d/copy-address", .rm32="*(ebp+" inouts[0].stack-offset "+" offset(f) ")", .r32=outputs[0]} => "8d/copy-address *(ebp+" var2.stack-offset "+" offset(f) ") " reg "/r32"
vim:ft=c:nowrap vim:ft=mu:nowrap:tw&