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: case OR:
switch operands := x.Data.(type) { switch operands := x.Data.(type) {
case *RegisterNumber: case *RegisterNumber:

View File

@ -4,12 +4,31 @@ type Mnemonic uint8
const ( const (
NONE Mnemonic = iota NONE Mnemonic = iota
// Arithmetic
ADD ADD
AND
CALL
COMMENT
COMPARE
DIV DIV
MODULO
MUL
NEGATE
SUB
// Bitwise operations
AND
OR
SHIFTL
SHIFTRS
XOR
// Data movement
MOVE
LOAD
POP
PUSH
STORE
// Control flow
CALL
JE JE
JNE JNE
JG JG
@ -17,21 +36,13 @@ const (
JL JL
JLE JLE
JUMP JUMP
MUL
LABEL LABEL
LOAD
MODULO
MOVE
OR
POP
PUSH
RETURN RETURN
SHIFTL
SHIFTRS
STORE
SUB
SYSCALL SYSCALL
XOR
// Others
COMMENT
COMPARE
) )
// String returns a human readable version. // String returns a human readable version.
@ -73,6 +84,8 @@ func (m Mnemonic) String() string {
return "move" return "move"
case MUL: case MUL:
return "mul" return "mul"
case NEGATE:
return "negate"
case OR: case OR:
return "or" return "or"
case POP: 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 package core
import ( import (
"fmt"
"git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/asm"
"git.akyoto.dev/cli/q/src/build/ast" "git.akyoto.dev/cli/q/src/build/ast"
"git.akyoto.dev/cli/q/src/build/cpu" "git.akyoto.dev/cli/q/src/build/cpu"
@ -24,10 +26,21 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
return err 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()) 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] left := node.Children[0]
right := node.Children[1] right := node.Children[1]
final := register final := register

View File

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

View File

@ -43,6 +43,11 @@ func (t Token) IsOperator() bool {
return t.Kind > _operators && t.Kind < _operatorsEnd 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. // Reset resets the token to default values.
func (t *Token) Reset() { func (t *Token) Reset() {
t.Position = 0 t.Position = 0

View File

@ -27,7 +27,7 @@ func Tokenize(buffer []byte) List {
case '\n': case '\n':
tokens = append(tokens, Token{Kind: NewLine, Position: i, Length: 1}) tokens = append(tokens, Token{Kind: NewLine, Position: i, Length: 1})
case '-': 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}) tokens = append(tokens, Token{Kind: Negate, Position: i, Length: 1})
} else { } else {
if i+1 < Position(len(buffer)) && buffer[i+1] == '=' { 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}, {"loop-lifetime", "", "", 0},
{"assert", "", "", 1}, {"assert", "", "", 1},
{"negative", "", "", 32}, {"negative", "", "", 32},
{"negation", "", "", 32},
} }
func TestPrograms(t *testing.T) { func TestPrograms(t *testing.T) {