Implemented negation

This commit is contained in:
Eduard Urbach 2024-07-28 15:42:51 +02:00
parent 6861ae9a90
commit bb74c0cf50
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
11 changed files with 136 additions and 21 deletions

View File

@ -0,0 +1,8 @@
package x64
import "git.akyoto.dev/cli/q/src/build/cpu"
// NegateRegister negates the value in the register.
func NegateRegister(code []byte, register cpu.Register) []byte {
return encode(code, AddressDirect, 0b011, register, 8, 0xF7)
}

View File

@ -0,0 +1,39 @@
package x64_test
import (
"testing"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/go/assert"
)
func TestNegateRegister(t *testing.T) {
usagePatterns := []struct {
Register cpu.Register
Code []byte
}{
{x64.RAX, []byte{0x48, 0xF7, 0xD8}},
{x64.RCX, []byte{0x48, 0xF7, 0xD9}},
{x64.RDX, []byte{0x48, 0xF7, 0xDA}},
{x64.RBX, []byte{0x48, 0xF7, 0xDB}},
{x64.RSP, []byte{0x48, 0xF7, 0xDC}},
{x64.RBP, []byte{0x48, 0xF7, 0xDD}},
{x64.RSI, []byte{0x48, 0xF7, 0xDE}},
{x64.RDI, []byte{0x48, 0xF7, 0xDF}},
{x64.R8, []byte{0x49, 0xF7, 0xD8}},
{x64.R9, []byte{0x49, 0xF7, 0xD9}},
{x64.R10, []byte{0x49, 0xF7, 0xDA}},
{x64.R11, []byte{0x49, 0xF7, 0xDB}},
{x64.R12, []byte{0x49, 0xF7, 0xDC}},
{x64.R13, []byte{0x49, 0xF7, 0xDD}},
{x64.R14, []byte{0x49, 0xF7, 0xDE}},
{x64.R15, []byte{0x49, 0xF7, 0xDF}},
}
for _, pattern := range usagePatterns {
t.Logf("neg %s", pattern.Register)
code := x64.NegateRegister(nil, pattern.Register)
assert.DeepEqual(t, code, pattern.Code)
}
}

View File

@ -165,6 +165,12 @@ func (a Assembler) Finalize() ([]byte, []byte) {
})
}
case NEGATE:
switch operands := x.Data.(type) {
case *Register:
code = x64.NegateRegister(code, operands.Register)
}
case OR:
switch operands := x.Data.(type) {
case *RegisterNumber:

View File

@ -4,12 +4,31 @@ type Mnemonic uint8
const (
NONE Mnemonic = iota
// Arithmetic
ADD
AND
CALL
COMMENT
COMPARE
DIV
MODULO
MUL
NEGATE
SUB
// Bitwise operations
AND
OR
SHIFTL
SHIFTRS
XOR
// Data movement
MOVE
LOAD
POP
PUSH
STORE
// Control flow
CALL
JE
JNE
JG
@ -17,21 +36,13 @@ const (
JL
JLE
JUMP
MUL
LABEL
LOAD
MODULO
MOVE
OR
POP
PUSH
RETURN
SHIFTL
SHIFTRS
STORE
SUB
SYSCALL
XOR
// Others
COMMENT
COMPARE
)
// String returns a human readable version.
@ -73,6 +84,8 @@ func (m Mnemonic) String() string {
return "move"
case MUL:
return "mul"
case NEGATE:
return "negate"
case OR:
return "or"
case POP:

View File

@ -0,0 +1,21 @@
package core
import (
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/cpu"
"git.akyoto.dev/cli/q/src/build/errors"
"git.akyoto.dev/cli/q/src/build/token"
)
// ExecuteRegister performs an operation on a single register.
func (f *Function) ExecuteRegister(operation token.Token, register cpu.Register) error {
switch operation.Kind {
case token.Negate:
f.Register(asm.NEGATE, register)
default:
return errors.New(&errors.InvalidOperator{Operator: operation.Text(f.File.Bytes)}, f.File, operation.Position)
}
return nil
}

View File

@ -1,6 +1,8 @@
package core
import (
"fmt"
"git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/cpu"
@ -24,10 +26,21 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
return err
}
if len(node.Children) < 2 {
if len(node.Children) == 1 {
if !node.Token.IsUnaryOperator() {
fmt.Println(node.String(f.File.Bytes), node.Token.Kind)
return errors.New(errors.MissingOperand, f.File, node.Token.End())
}
err := f.ExpressionToRegister(node.Children[0], register)
if err != nil {
return err
}
return f.ExecuteRegister(node.Token, register)
}
left := node.Children[0]
right := node.Children[1]
final := register

View File

@ -38,8 +38,6 @@ const (
Shr // >>
LogicalAnd // &&
LogicalOr // ||
Not // ! (unary)
Negate // - (unary)
Equal // ==
Less // <
Greater // >
@ -51,6 +49,10 @@ const (
Call // x()
Array // [x]
Separator // ,
_unary // <unary>
Not // ! (unary)
Negate // - (unary)
_unaryEnd // </unary>
_assignments // <assignments>
Assign // =
AddAssign // +=

View File

@ -43,6 +43,11 @@ func (t Token) IsOperator() bool {
return t.Kind > _operators && t.Kind < _operatorsEnd
}
// IsUnaryOperator returns true if the token is a unary operator.
func (t Token) IsUnaryOperator() bool {
return t.Kind > _unary && t.Kind < _unaryEnd
}
// Reset resets the token to default values.
func (t *Token) Reset() {
t.Position = 0

View File

@ -27,7 +27,7 @@ func Tokenize(buffer []byte) List {
case '\n':
tokens = append(tokens, Token{Kind: NewLine, Position: i, Length: 1})
case '-':
if len(tokens) == 0 || tokens[len(tokens)-1].IsOperator() || tokens[len(tokens)-1].IsExpressionStart() {
if len(tokens) == 0 || tokens[len(tokens)-1].IsOperator() || tokens[len(tokens)-1].IsExpressionStart() || tokens[len(tokens)-1].IsKeyword() {
tokens = append(tokens, Token{Kind: Negate, Position: i, Length: 1})
} else {
if i+1 < Position(len(buffer)) && buffer[i+1] == '=' {

View File

@ -0,0 +1,7 @@
main() {
syscall(60, f(-32))
}
f(x) {
return -x
}

View File

@ -43,6 +43,7 @@ var programs = []struct {
{"loop-lifetime", "", "", 0},
{"assert", "", "", 1},
{"negative", "", "", 32},
{"negation", "", "", 32},
}
func TestPrograms(t *testing.T) {