Implemented simple expressions

This commit is contained in:
Eduard Urbach 2024-06-25 20:50:06 +02:00
parent 2569c1bf63
commit e6462266ef
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
12 changed files with 232 additions and 52 deletions

View File

@ -16,6 +16,7 @@ hello() {
length -= 20 length -= 20
length *= 10 length *= 10
length /= 100 length /= 100
length = (0 + 50 - 20) * 10 / 100
loop { loop {
syscall(write, stdout, address, length) syscall(write, stdout, address, length)

View File

@ -11,7 +11,6 @@ import (
// CompileAssignment compiles an assignment. // CompileAssignment compiles an assignment.
func (f *Function) CompileAssignment(expr *expression.Expression) error { func (f *Function) CompileAssignment(expr *expression.Expression) error {
name := expr.Children[0].Token.Text() name := expr.Children[0].Token.Text()
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
register := f.Variables[name].Register register := f.Variables[name].Register
switch expr.Token.Text() { switch expr.Token.Text() {
@ -19,15 +18,19 @@ func (f *Function) CompileAssignment(expr *expression.Expression) error {
f.ExpressionToRegister(expr.Children[1], register) f.ExpressionToRegister(expr.Children[1], register)
case "+=": case "+=":
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
f.Assembler.RegisterNumber(asm.ADD, register, number) f.Assembler.RegisterNumber(asm.ADD, register, number)
case "-=": case "-=":
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
f.Assembler.RegisterNumber(asm.SUB, register, number) f.Assembler.RegisterNumber(asm.SUB, register, number)
case "*=": case "*=":
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
f.Assembler.RegisterNumber(asm.MUL, register, number) f.Assembler.RegisterNumber(asm.MUL, register, number)
case "/=": case "/=":
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
f.Assembler.RegisterNumber(asm.DIV, register, number) f.Assembler.RegisterNumber(asm.DIV, register, number)
default: default:

View File

@ -120,27 +120,61 @@ func (f *Function) CompileInstruction(line token.List) error {
return errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text()}, f.File, expr.Token.Position) return errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text()}, f.File, expr.Token.Position)
} }
// isAssignment returns true if the expression is an assignment.
func isAssignment(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Bytes[len(expr.Token.Bytes)-1] == '='
}
// isFunctionCall returns true if the expression is a function call.
func isFunctionCall(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Text() == "λ"
}
// isVariableDefinition returns true if the expression is a variable definition.
func isVariableDefinition(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Text() == ":="
}
// ExpressionToRegister moves the result of an expression into the given register. // ExpressionToRegister moves the result of an expression into the given register.
func (f *Function) ExpressionToRegister(root *expression.Expression, register cpu.Register) error { func (f *Function) ExpressionToRegister(root *expression.Expression, register cpu.Register) error {
if root.IsLeaf() { if root.IsLeaf() {
return f.TokenToRegister(root.Token, register) return f.TokenToRegister(root.Token, register)
} }
left := root.Children[0]
right := root.Children[1]
f.ExpressionToRegister(left, register)
if right.IsLeaf() {
value := right.Token.Text()
n, err := strconv.Atoi(value)
if err != nil {
return err
}
switch root.Token.Text() {
case "+":
f.Assembler.RegisterNumber(asm.ADD, register, n)
case "-":
f.Assembler.RegisterNumber(asm.SUB, register, n)
case "*":
f.Assembler.RegisterNumber(asm.MUL, register, n)
case "/":
f.Assembler.RegisterNumber(asm.DIV, register, n)
}
return nil
} else {
temporary, _ := f.CPU.FindFree()
f.CPU.Use(temporary)
f.ExpressionToRegister(right, temporary)
f.CPU.Free(temporary)
switch root.Token.Text() {
case "+":
f.Assembler.RegisterRegister(asm.ADD, register, temporary)
case "-":
f.Assembler.RegisterRegister(asm.SUB, register, temporary)
case "*":
f.Assembler.RegisterRegister(asm.MUL, register, temporary)
case "/":
f.Assembler.RegisterRegister(asm.DIV, register, temporary)
}
}
return errors.New(errors.NotImplemented, f.File, root.Token.Position) return errors.New(errors.NotImplemented, f.File, root.Token.Position)
} }
@ -207,3 +241,18 @@ func (f *Function) identifierExists(name string) bool {
_, exists := f.Variables[name] _, exists := f.Variables[name]
return exists return exists
} }
// isAssignment returns true if the expression is an assignment.
func isAssignment(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Bytes[len(expr.Token.Bytes)-1] == '='
}
// isFunctionCall returns true if the expression is a function call.
func isFunctionCall(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Text() == "λ"
}
// isVariableDefinition returns true if the expression is a variable definition.
func isVariableDefinition(expr *expression.Expression) bool {
return expr.Token.Kind == token.Operator && expr.Token.Text() == ":="
}

View File

@ -6,5 +6,37 @@ import (
// AddRegNum adds a number to the given register. // AddRegNum adds a number to the given register.
func AddRegNum(code []byte, destination cpu.Register, number int) []byte { func AddRegNum(code []byte, destination cpu.Register, number int) []byte {
return numRegReg(code, 0x83, 0x81, 0, byte(destination), number) return numRegReg(code, 0, byte(destination), number, 0x83, 0x81)
}
// AddRegReg adds a number to the given register.
func AddRegReg(code []byte, destination cpu.Register, operand cpu.Register) []byte {
return regReg(code, byte(operand), byte(destination), 0x01)
}
func regReg(code []byte, reg byte, rm byte, opCodes ...byte) []byte {
w := byte(1) // Indicates a 64-bit register.
r := byte(0) // Extension to the "reg" field in ModRM.
x := byte(0) // Extension to the SIB index field.
b := byte(0) // Extension to the "rm" field in ModRM or the SIB base (r8 up to r15 use this).
mod := byte(0b11) // Direct addressing mode, no register offsets.
if reg > 0b111 {
r = 1
reg &= 0b111
}
if rm > 0b111 {
b = 1
rm &= 0b111
}
rex := REX(w, r, x, b)
modRM := ModRM(mod, reg, rm)
code = append(code, rex)
code = append(code, opCodes...)
code = append(code, modRM)
return code
} }

View File

@ -55,3 +55,34 @@ func TestAddRegisterNumber(t *testing.T) {
assert.DeepEqual(t, code, pattern.Code) assert.DeepEqual(t, code, pattern.Code)
} }
} }
func TestAddRegisterRegister(t *testing.T) {
usagePatterns := []struct {
Left cpu.Register
Right cpu.Register
Code []byte
}{
{x64.RAX, x64.R15, []byte{0x4C, 0x01, 0xF8}},
{x64.RCX, x64.R14, []byte{0x4C, 0x01, 0xF1}},
{x64.RDX, x64.R13, []byte{0x4C, 0x01, 0xEA}},
{x64.RBX, x64.R12, []byte{0x4C, 0x01, 0xE3}},
{x64.RSP, x64.R11, []byte{0x4C, 0x01, 0xDC}},
{x64.RBP, x64.R10, []byte{0x4C, 0x01, 0xD5}},
{x64.RSI, x64.R9, []byte{0x4C, 0x01, 0xCE}},
{x64.RDI, x64.R8, []byte{0x4C, 0x01, 0xC7}},
{x64.R8, x64.RDI, []byte{0x49, 0x01, 0xF8}},
{x64.R9, x64.RSI, []byte{0x49, 0x01, 0xF1}},
{x64.R10, x64.RBP, []byte{0x49, 0x01, 0xEA}},
{x64.R11, x64.RSP, []byte{0x49, 0x01, 0xE3}},
{x64.R12, x64.RBX, []byte{0x49, 0x01, 0xDC}},
{x64.R13, x64.RDX, []byte{0x49, 0x01, 0xD5}},
{x64.R14, x64.RCX, []byte{0x49, 0x01, 0xCE}},
{x64.R15, x64.RAX, []byte{0x49, 0x01, 0xC7}},
}
for _, pattern := range usagePatterns {
t.Logf("add %s, %s", pattern.Left, pattern.Right)
code := x64.AddRegReg(nil, pattern.Left, pattern.Right)
assert.DeepEqual(t, code, pattern.Code)
}
}

View File

@ -4,5 +4,10 @@ import "git.akyoto.dev/cli/q/src/build/cpu"
// MulRegNum multiplies a register with a number. // MulRegNum multiplies a register with a number.
func MulRegNum(code []byte, destination cpu.Register, number int) []byte { func MulRegNum(code []byte, destination cpu.Register, number int) []byte {
return numRegReg(code, 0x6B, 0x69, byte(destination), byte(destination), number) return numRegReg(code, byte(destination), byte(destination), number, 0x6B, 0x69)
}
// MulRegReg multiplies a register with another register.
func MulRegReg(code []byte, destination cpu.Register, operand cpu.Register) []byte {
return regReg(code, byte(destination), byte(operand), 0x0F, 0xAF)
} }

View File

@ -55,3 +55,34 @@ func TestMulRegisterNumber(t *testing.T) {
assert.DeepEqual(t, code, pattern.Code) assert.DeepEqual(t, code, pattern.Code)
} }
} }
func TestMulRegisterRegister(t *testing.T) {
usagePatterns := []struct {
Left cpu.Register
Right cpu.Register
Code []byte
}{
{x64.RAX, x64.R15, []byte{0x49, 0x0F, 0xAF, 0xC7}},
{x64.RCX, x64.R14, []byte{0x49, 0x0F, 0xAF, 0xCE}},
{x64.RDX, x64.R13, []byte{0x49, 0x0F, 0xAF, 0xD5}},
{x64.RBX, x64.R12, []byte{0x49, 0x0F, 0xAF, 0xDC}},
{x64.RSP, x64.R11, []byte{0x49, 0x0F, 0xAF, 0xE3}},
{x64.RBP, x64.R10, []byte{0x49, 0x0F, 0xAF, 0xEA}},
{x64.RSI, x64.R9, []byte{0x49, 0x0F, 0xAF, 0xF1}},
{x64.RDI, x64.R8, []byte{0x49, 0x0F, 0xAF, 0xF8}},
{x64.R8, x64.RDI, []byte{0x4C, 0x0F, 0xAF, 0xC7}},
{x64.R9, x64.RSI, []byte{0x4C, 0x0F, 0xAF, 0xCE}},
{x64.R10, x64.RBP, []byte{0x4C, 0x0F, 0xAF, 0xD5}},
{x64.R11, x64.RSP, []byte{0x4C, 0x0F, 0xAF, 0xDC}},
{x64.R12, x64.RBX, []byte{0x4C, 0x0F, 0xAF, 0xE3}},
{x64.R13, x64.RDX, []byte{0x4C, 0x0F, 0xAF, 0xEA}},
{x64.R14, x64.RCX, []byte{0x4C, 0x0F, 0xAF, 0xF1}},
{x64.R15, x64.RAX, []byte{0x4C, 0x0F, 0xAF, 0xF8}},
}
for _, pattern := range usagePatterns {
t.Logf("mul %s, %s", pattern.Left, pattern.Right)
code := x64.MulRegReg(nil, pattern.Left, pattern.Right)
assert.DeepEqual(t, code, pattern.Code)
}
}

View File

@ -6,5 +6,10 @@ import (
// SubRegNum subtracts a number from the given register. // SubRegNum subtracts a number from the given register.
func SubRegNum(code []byte, destination cpu.Register, number int) []byte { func SubRegNum(code []byte, destination cpu.Register, number int) []byte {
return numRegReg(code, 0x83, 0x81, 0b101, byte(destination), number) return numRegReg(code, 0b101, byte(destination), number, 0x83, 0x81)
}
// SubRegReg subtracts a register value from another register.
func SubRegReg(code []byte, destination cpu.Register, operand cpu.Register) []byte {
return regReg(code, byte(operand), byte(destination), 0x29)
} }

View File

@ -1,41 +1,15 @@
package x64 package x64
// numRegReg encodes an instruction with up to two registers and a number parameter. // numRegReg encodes an instruction with up to two registers and a number parameter.
func numRegReg(code []byte, opCode8 byte, opCode32 byte, reg byte, rm byte, number int) []byte { func numRegReg(code []byte, reg byte, rm byte, number int, opCode8 byte, opCode32 byte) []byte {
w := byte(1) // Indicates a 64-bit register.
r := byte(0) // Extension to the "reg" field in ModRM.
x := byte(0) // Extension to the SIB index field.
b := byte(0) // Extension to the "rm" field in ModRM or the SIB base (r8 up to r15 use this).
mod := byte(0b11) // Direct addressing mode, no register offsets.
if reg > 0b111 {
r = 1
reg &= 0b111
}
if rm > 0b111 {
b = 1
rm &= 0b111
}
rex := REX(w, r, x, b)
modRM := ModRM(mod, reg, rm)
if sizeOf(number) == 1 { if sizeOf(number) == 1 {
return append( code = regReg(code, reg, rm, opCode8)
code, return append(code, byte(number))
rex,
opCode8,
modRM,
byte(number),
)
} }
code = regReg(code, reg, rm, opCode32)
return append( return append(
code, code,
rex,
opCode32,
modRM,
byte(number), byte(number),
byte(number>>8), byte(number>>8),
byte(number>>16), byte(number>>16),

View File

@ -31,18 +31,24 @@ func (a *Assembler) Finalize() ([]byte, []byte) {
switch operands := x.Data.(type) { switch operands := x.Data.(type) {
case *RegisterNumber: case *RegisterNumber:
code = x64.AddRegNum(code, operands.Register, operands.Number) code = x64.AddRegNum(code, operands.Register, operands.Number)
case *RegisterRegister:
code = x64.AddRegReg(code, operands.Destination, operands.Source)
} }
case SUB: case SUB:
switch operands := x.Data.(type) { switch operands := x.Data.(type) {
case *RegisterNumber: case *RegisterNumber:
code = x64.SubRegNum(code, operands.Register, operands.Number) code = x64.SubRegNum(code, operands.Register, operands.Number)
case *RegisterRegister:
code = x64.SubRegReg(code, operands.Destination, operands.Source)
} }
case MUL: case MUL:
switch operands := x.Data.(type) { switch operands := x.Data.(type) {
case *RegisterNumber: case *RegisterNumber:
code = x64.MulRegNum(code, operands.Register, operands.Number) code = x64.MulRegNum(code, operands.Register, operands.Number)
case *RegisterRegister:
code = x64.MulRegReg(code, operands.Destination, operands.Source)
} }
case DIV: case DIV:
@ -65,6 +71,16 @@ func (a *Assembler) Finalize() ([]byte, []byte) {
code = x64.PopReg(code, x64.RDX) code = x64.PopReg(code, x64.RDX)
code = x64.PopReg(code, x64.RAX) code = x64.PopReg(code, x64.RAX)
} }
case *RegisterRegister:
code = x64.PushReg(code, x64.RAX)
code = x64.PushReg(code, x64.RDX)
code = x64.MoveRegReg64(code, x64.RAX, operands.Destination)
code = x64.ExtendRAXToRDX(code)
code = x64.DivReg(code, operands.Source)
code = x64.MoveRegReg64(code, operands.Destination, x64.RAX)
code = x64.PopReg(code, x64.RDX)
code = x64.PopReg(code, x64.RAX)
} }
case CALL: case CALL:

View File

@ -13,6 +13,17 @@ func (a *Assembler) RegisterNumber(mnemonic Mnemonic, reg cpu.Register, number i
}) })
} }
// RegisterRegister adds an instruction using two registers.
func (a *Assembler) RegisterRegister(mnemonic Mnemonic, left cpu.Register, right cpu.Register) {
a.Instructions = append(a.Instructions, Instruction{
Mnemonic: mnemonic,
Data: &RegisterRegister{
Destination: left,
Source: right,
},
})
}
// MoveRegisterRegister moves a register value into another register. // MoveRegisterRegister moves a register value into another register.
func (a *Assembler) MoveRegisterRegister(destination cpu.Register, source cpu.Register) { func (a *Assembler) MoveRegisterRegister(destination cpu.Register, source cpu.Register) {
a.Instructions = append(a.Instructions, Instruction{ a.Instructions = append(a.Instructions, Instruction{

View File

@ -55,13 +55,13 @@ func (expr *Expression) Close() {
} }
// EachLeaf iterates through all leaves in the tree. // EachLeaf iterates through all leaves in the tree.
func (expr *Expression) EachLeaf(callBack func(*Expression) error) error { func (expr *Expression) EachLeaf(call func(*Expression) error) error {
if expr.IsLeaf() { if expr.IsLeaf() {
return callBack(expr) return call(expr)
} }
for _, child := range expr.Children { for _, child := range expr.Children {
err := child.EachLeaf(callBack) err := child.EachLeaf(call)
if err != nil { if err != nil {
return err return err
@ -71,6 +71,28 @@ func (expr *Expression) EachLeaf(callBack func(*Expression) error) error {
return nil return nil
} }
// EachOperation iterates through all the operations in the tree.
func (expr *Expression) EachOperation(call func(*Expression) error) error {
if expr.IsLeaf() {
return nil
}
// Don't descend into the parameters of function calls
if expr.Token.Text() == "λ" {
return call(expr)
}
for _, child := range expr.Children {
err := child.EachOperation(call)
if err != nil {
return err
}
}
return call(expr)
}
// RemoveChild removes a child from the expression. // RemoveChild removes a child from the expression.
func (expr *Expression) RemoveChild(child *Expression) { func (expr *Expression) RemoveChild(child *Expression) {
for i, c := range expr.Children { for i, c := range expr.Children {