From a7d8d6ae20921e4f7c12ad931a33bc9d9234fbfe Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 25 Jul 2024 14:17:51 +0200 Subject: [PATCH] Implemented bitwise operations --- README.md | 8 +-- src/build/arch/x64/And.go | 15 ++++++ src/build/arch/x64/Or.go | 15 ++++++ src/build/arch/x64/Xor.go | 15 ++++++ src/build/asm/Finalize.go | 24 +++++++++ src/build/asm/Mnemonic.go | 9 ++++ src/build/core/ExecuteRegisterNumber.go | 9 ++++ src/build/core/ExecuteRegisterRegister.go | 9 ++++ tests/programs/bitwise-and.q | 61 +++++++++++++++++++++++ tests/programs/bitwise-or.q | 61 +++++++++++++++++++++++ tests/programs/bitwise-xor.q | 61 +++++++++++++++++++++++ tests/programs_test.go | 5 +- 12 files changed, 287 insertions(+), 5 deletions(-) create mode 100644 src/build/arch/x64/And.go create mode 100644 src/build/arch/x64/Or.go create mode 100644 src/build/arch/x64/Xor.go create mode 100644 tests/programs/bitwise-and.q create mode 100644 tests/programs/bitwise-or.q create mode 100644 tests/programs/bitwise-xor.q diff --git a/README.md b/README.md index 89f7fe0..8fcdcac 100644 --- a/README.md +++ b/README.md @@ -150,12 +150,12 @@ This is what generates expressions from tokens. - [x] `=`, `:=` - [x] `+`, `-`, `*`, `/`, `%` - [x] `+=`, `-=`, `*=`, `/=`, `%=` -- [ ] `&`, `|`, `^` -- [ ] `&=`, `|=`, `^=` -- [ ] `<<`, `>>` -- [ ] `<<=`, `>>=` +- [x] `&`, `|`, `^` +- [x] `&=`, `|=`, `^=` - [x] `==`, `!=`, `<`, `<=`, `>`, `>=` - [x] `&&`, `||` +- [ ] `<<`, `>>` +- [ ] `<<=`, `>>=` ### Architecture diff --git a/src/build/arch/x64/And.go b/src/build/arch/x64/And.go new file mode 100644 index 0000000..e5d8fcb --- /dev/null +++ b/src/build/arch/x64/And.go @@ -0,0 +1,15 @@ +package x64 + +import ( + "git.akyoto.dev/cli/q/src/build/cpu" +) + +// AndRegisterNumber performs a bitwise AND using a register and a number. +func AndRegisterNumber(code []byte, destination cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b100, destination, number, 0x83, 0x81) +} + +// AndRegisterRegister performs a bitwise AND using two registers. +func AndRegisterRegister(code []byte, destination cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, operand, destination, 8, 0x21) +} diff --git a/src/build/arch/x64/Or.go b/src/build/arch/x64/Or.go new file mode 100644 index 0000000..2252925 --- /dev/null +++ b/src/build/arch/x64/Or.go @@ -0,0 +1,15 @@ +package x64 + +import ( + "git.akyoto.dev/cli/q/src/build/cpu" +) + +// OrRegisterNumber performs a bitwise OR using a register and a number. +func OrRegisterNumber(code []byte, destination cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b001, destination, number, 0x83, 0x81) +} + +// OrRegisterRegister performs a bitwise OR using two registers. +func OrRegisterRegister(code []byte, destination cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, operand, destination, 8, 0x09) +} diff --git a/src/build/arch/x64/Xor.go b/src/build/arch/x64/Xor.go new file mode 100644 index 0000000..305dce2 --- /dev/null +++ b/src/build/arch/x64/Xor.go @@ -0,0 +1,15 @@ +package x64 + +import ( + "git.akyoto.dev/cli/q/src/build/cpu" +) + +// XorRegisterNumber performs a bitwise XOR using a register and a number. +func XorRegisterNumber(code []byte, destination cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b110, destination, number, 0x83, 0x81) +} + +// XorRegisterRegister performs a bitwise XOR using two registers. +func XorRegisterRegister(code []byte, destination cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, operand, destination, 8, 0x31) +} diff --git a/src/build/asm/Finalize.go b/src/build/asm/Finalize.go index 0d68d91..c7bdc94 100644 --- a/src/build/asm/Finalize.go +++ b/src/build/asm/Finalize.go @@ -26,6 +26,14 @@ func (a Assembler) Finalize() ([]byte, []byte) { code = x64.AddRegisterRegister(code, operands.Destination, operands.Source) } + case AND: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.AndRegisterNumber(code, operands.Register, operands.Number) + case *RegisterRegister: + code = x64.AndRegisterRegister(code, operands.Destination, operands.Source) + } + case SUB: switch operands := x.Data.(type) { case *RegisterNumber: @@ -153,6 +161,14 @@ func (a Assembler) Finalize() ([]byte, []byte) { }) } + case OR: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.OrRegisterNumber(code, operands.Register, operands.Number) + case *RegisterRegister: + code = x64.OrRegisterRegister(code, operands.Destination, operands.Source) + } + case POP: switch operands := x.Data.(type) { case *Register: @@ -177,6 +193,14 @@ func (a Assembler) Finalize() ([]byte, []byte) { case SYSCALL: code = x64.Syscall(code) + case XOR: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.XorRegisterNumber(code, operands.Register, operands.Number) + case *RegisterRegister: + code = x64.XorRegisterRegister(code, operands.Destination, operands.Source) + } + default: panic("unknown mnemonic: " + x.Mnemonic.String()) } diff --git a/src/build/asm/Mnemonic.go b/src/build/asm/Mnemonic.go index 223666a..7ed32ca 100644 --- a/src/build/asm/Mnemonic.go +++ b/src/build/asm/Mnemonic.go @@ -5,6 +5,7 @@ type Mnemonic uint8 const ( NONE Mnemonic = iota ADD + AND CALL COMMENT COMPARE @@ -21,12 +22,14 @@ const ( LOAD MODULO MOVE + OR POP PUSH RETURN STORE SUB SYSCALL + XOR ) // String returns a human readable version. @@ -34,6 +37,8 @@ func (m Mnemonic) String() string { switch m { case ADD: return "add" + case AND: + return "and" case CALL: return "call" case COMMENT: @@ -66,6 +71,8 @@ func (m Mnemonic) String() string { return "move" case MUL: return "mul" + case OR: + return "or" case POP: return "pop" case PUSH: @@ -78,6 +85,8 @@ func (m Mnemonic) String() string { return "store" case SYSCALL: return "syscall" + case XOR: + return "xor" default: return "" } diff --git a/src/build/core/ExecuteRegisterNumber.go b/src/build/core/ExecuteRegisterNumber.go index d870264..6f29527 100644 --- a/src/build/core/ExecuteRegisterNumber.go +++ b/src/build/core/ExecuteRegisterNumber.go @@ -25,6 +25,15 @@ func (f *Function) ExecuteRegisterNumber(operation token.Token, register cpu.Reg case token.Mod, token.ModAssign: f.RegisterNumber(asm.MODULO, register, number) + case token.And, token.AndAssign: + f.RegisterNumber(asm.AND, register, number) + + case token.Or, token.OrAssign: + f.RegisterNumber(asm.OR, register, number) + + case token.Xor, token.XorAssign: + f.RegisterNumber(asm.XOR, register, number) + case token.Equal, token.NotEqual, token.Less, token.LessEqual, token.Greater, token.GreaterEqual: f.RegisterNumber(asm.COMPARE, register, number) diff --git a/src/build/core/ExecuteRegisterRegister.go b/src/build/core/ExecuteRegisterRegister.go index 97ee94b..bea03e9 100644 --- a/src/build/core/ExecuteRegisterRegister.go +++ b/src/build/core/ExecuteRegisterRegister.go @@ -25,6 +25,15 @@ func (f *Function) ExecuteRegisterRegister(operation token.Token, destination cp case token.Mod, token.ModAssign: f.RegisterRegister(asm.MODULO, destination, source) + case token.And, token.AndAssign: + f.RegisterRegister(asm.AND, destination, source) + + case token.Or, token.OrAssign: + f.RegisterRegister(asm.OR, destination, source) + + case token.Xor, token.XorAssign: + f.RegisterRegister(asm.XOR, destination, source) + case token.Equal, token.NotEqual, token.Less, token.LessEqual, token.Greater, token.GreaterEqual: f.RegisterRegister(asm.COMPARE, destination, source) diff --git a/tests/programs/bitwise-and.q b/tests/programs/bitwise-and.q new file mode 100644 index 0000000..fda2dcc --- /dev/null +++ b/tests/programs/bitwise-and.q @@ -0,0 +1,61 @@ +import sys + +main() { + if 0 & 0 != 0 { + sys.exit(1) + } + + if 0 & 1 != 0 { + sys.exit(2) + } + + if 1 & 0 != 0 { + sys.exit(3) + } + + if 1 & 1 != 1 { + sys.exit(4) + } + + if 1 & 2 != 0 { + sys.exit(5) + } + + if 1 & 3 != 1 { + sys.exit(6) + } + + if 2 & 0 != 0 { + sys.exit(7) + } + + if 2 & 1 != 0 { + sys.exit(8) + } + + if 2 & 2 != 2 { + sys.exit(9) + } + + if 2 & 3 != 2 { + sys.exit(10) + } + + if 3 & 0 != 0 { + sys.exit(11) + } + + if 3 & 1 != 1 { + sys.exit(12) + } + + if 3 & 2 != 2 { + sys.exit(13) + } + + if 3 & 3 != 3 { + sys.exit(14) + } + + sys.exit(0) +} \ No newline at end of file diff --git a/tests/programs/bitwise-or.q b/tests/programs/bitwise-or.q new file mode 100644 index 0000000..6ea338f --- /dev/null +++ b/tests/programs/bitwise-or.q @@ -0,0 +1,61 @@ +import sys + +main() { + if 0 | 0 != 0 { + sys.exit(1) + } + + if 0 | 1 != 1 { + sys.exit(2) + } + + if 1 | 0 != 1 { + sys.exit(3) + } + + if 1 | 1 != 1 { + sys.exit(4) + } + + if 1 | 2 != 3 { + sys.exit(5) + } + + if 1 | 3 != 3 { + sys.exit(6) + } + + if 2 | 0 != 2 { + sys.exit(7) + } + + if 2 | 1 != 3 { + sys.exit(8) + } + + if 2 | 2 != 2 { + sys.exit(9) + } + + if 2 | 3 != 3 { + sys.exit(10) + } + + if 3 | 0 != 3 { + sys.exit(11) + } + + if 3 | 1 != 3 { + sys.exit(12) + } + + if 3 | 2 != 3 { + sys.exit(13) + } + + if 3 | 3 != 3 { + sys.exit(14) + } + + sys.exit(0) +} \ No newline at end of file diff --git a/tests/programs/bitwise-xor.q b/tests/programs/bitwise-xor.q new file mode 100644 index 0000000..f648051 --- /dev/null +++ b/tests/programs/bitwise-xor.q @@ -0,0 +1,61 @@ +import sys + +main() { + if 0 ^ 0 != 0 { + sys.exit(1) + } + + if 0 ^ 1 != 1 { + sys.exit(2) + } + + if 1 ^ 0 != 1 { + sys.exit(3) + } + + if 1 ^ 1 != 0 { + sys.exit(4) + } + + if 1 ^ 2 != 3 { + sys.exit(5) + } + + if 1 ^ 3 != 2 { + sys.exit(6) + } + + if 2 ^ 0 != 2 { + sys.exit(7) + } + + if 2 ^ 1 != 3 { + sys.exit(8) + } + + if 2 ^ 2 != 0 { + sys.exit(9) + } + + if 2 ^ 3 != 1 { + sys.exit(10) + } + + if 3 ^ 0 != 3 { + sys.exit(11) + } + + if 3 ^ 1 != 2 { + sys.exit(12) + } + + if 3 ^ 2 != 1 { + sys.exit(13) + } + + if 3 ^ 3 != 0 { + sys.exit(14) + } + + sys.exit(0) +} \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index 08a1e12..6eac176 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -32,10 +32,13 @@ var programs = []struct { {"branch-and", "", "", 0}, {"branch-or", "", "", 0}, {"branch-both", "", "", 0}, + {"bitwise-and", "", "", 0}, + {"bitwise-or", "", "", 0}, + {"bitwise-xor", "", "", 0}, + {"remainder", "", "", 0}, {"jump-near", "", "", 0}, {"loop", "", "", 0}, {"loop-lifetime", "", "", 0}, - {"remainder", "", "", 0}, } func TestPrograms(t *testing.T) {