From 5fd4909f81e5f3c6fbb8871f0d5bc1a1a3f95384 Mon Sep 17 00:00:00 2001 From: nub31 Date: Tue, 9 Sep 2025 16:15:40 +0200 Subject: [PATCH] Bit manipulation --- example/src/main.nub | 12 +- .../NubLang/Generation/QBE/QBEGenerator.cs | 230 +++++++++++------- src/compiler/NubLang/Parsing/Parser.cs | 50 +++- .../Parsing/Syntax/ExpressionSyntax.cs | 12 +- src/compiler/NubLang/Tokenization/Token.cs | 4 + .../NubLang/Tokenization/Tokenizer.cs | 4 + .../TypeChecking/Node/ExpressionNode.cs | 8 +- .../NubLang/TypeChecking/TypeChecker.cs | 84 ++++--- 8 files changed, 272 insertions(+), 132 deletions(-) diff --git a/example/src/main.nub b/example/src/main.nub index 3743d01..1178f56 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -4,11 +4,19 @@ extern func puts(text: cstring) func main(args: []cstring): i64 { let x: u32 = 23 - test(x) + + if x == 23 && true + { + puts("yes") + } + else + { + puts("no") + } + return 0 } func test(test: u32) { - puts("test") } \ No newline at end of file diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 52b4f11..a8d76de 100644 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -725,102 +725,162 @@ public class QBEGenerator var outputName = TmpName(); - var instruction = EmitBinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type, left, right); + var instruction = BinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type); _writer.Indented($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); return outputName; } - private string EmitBinaryInstructionFor(BinaryOperator op, TypeNode type, string left, string right) + private string BinaryInstructionFor(BinaryOperator op, TypeNode type) { - if (op is - BinaryOperator.Equal or - BinaryOperator.NotEqual or - BinaryOperator.GreaterThan or - BinaryOperator.GreaterThanOrEqual or - BinaryOperator.LessThan or - BinaryOperator.LessThanOrEqual) - { - char suffix; - - if (!type.IsSimpleType(out var simpleType, out _)) - { - throw new NotSupportedException("Binary operations is only supported for simple types."); - } - - switch (simpleType.StorageSize) - { - case StorageSize.I8: - _writer.Indented($"{left} =w extsb {left}"); - _writer.Indented($"{right} =w extsb {right}"); - suffix = 'w'; - break; - case StorageSize.U8: - _writer.Indented($"{left} =w extub {left}"); - _writer.Indented($"{right} =w extub {right}"); - suffix = 'w'; - break; - case StorageSize.I16: - _writer.Indented($"{left} =w extsh {left}"); - _writer.Indented($"{right} =w extsh {right}"); - suffix = 'w'; - break; - case StorageSize.U16: - _writer.Indented($"{left} =w extuh {left}"); - _writer.Indented($"{right} =w extuh {right}"); - suffix = 'w'; - break; - case StorageSize.I32 or StorageSize.U32: - suffix = 'w'; - break; - case StorageSize.I64 or StorageSize.U64: - suffix = 'l'; - break; - default: - throw new NotSupportedException($"Unsupported type '{simpleType}' for binary operator '{op}'"); - } - - if (op is BinaryOperator.Equal) - { - return "ceq" + suffix; - } - - if (op is BinaryOperator.NotEqual) - { - return "cne" + suffix; - } - - string sign; - - if (simpleType is IntTypeNode { Signed: true }) - { - sign = "s"; - } - else if (simpleType is IntTypeNode { Signed: false }) - { - sign = "u"; - } - else - { - throw new NotSupportedException($"Unsupported type '{type}' for binary operator '{op}'"); - } - - return op switch - { - BinaryOperator.GreaterThan => 'c' + sign + "gt" + suffix, - BinaryOperator.GreaterThanOrEqual => 'c' + sign + "ge" + suffix, - BinaryOperator.LessThan => 'c' + sign + "lt" + suffix, - BinaryOperator.LessThanOrEqual => 'c' + sign + "le" + suffix, - _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) - }; - } - return op switch { + BinaryOperator.RightShift => type switch + { + IntTypeNode { Signed: true } => "sar", + IntTypeNode { Signed: false } => "shr", + _ => throw new NotSupportedException($"Right shift not supported for type '{type}'") + }, + BinaryOperator.BitwiseAnd => "and", + BinaryOperator.BitwiseOr => "or", + BinaryOperator.BitwiseXor => "xor", + BinaryOperator.LeftShift => "shl", + BinaryOperator.Divide => type switch + { + IntTypeNode { Signed: true } => "div", + IntTypeNode { Signed: false } => "udiv", + FloatTypeNode => "div", + _ => throw new NotSupportedException($"Division not supported for type '{type}'") + }, + BinaryOperator.Modulo => type switch + { + IntTypeNode { Signed: true } => "rem", + IntTypeNode { Signed: false } => "urem", + _ => throw new NotSupportedException($"Modulo not supported for type '{type}'") + }, BinaryOperator.Plus => "add", BinaryOperator.Minus => "sub", BinaryOperator.Multiply => "mul", - BinaryOperator.Divide => "div", + BinaryOperator.Equal => type switch + { + IntTypeNode intType => intType.Width switch + { + <= 32 => "ceqw", + 64 => "ceql", + _ => throw new ArgumentOutOfRangeException() + }, + FloatTypeNode floatType => floatType.Width switch + { + 32 => "ceqs", + 64 => "ceqd", + _ => throw new ArgumentOutOfRangeException() + }, + _ => throw new NotSupportedException($"Equality comparison not supported for type '{type}'") + }, + BinaryOperator.NotEqual => type switch + { + IntTypeNode intType => intType.Width switch + { + <= 32 => "cnew", + 64 => "cnel", + _ => throw new ArgumentOutOfRangeException() + }, + FloatTypeNode floatType => floatType.Width switch + { + 32 => "cnes", + 64 => "cned", + _ => throw new ArgumentOutOfRangeException() + }, + _ => throw new NotSupportedException($"Inequality comparison not supported for type '{type}'") + }, + BinaryOperator.LessThan => type switch + { + IntTypeNode { Signed: true } intType => intType.Width switch + { + <= 32 => "csltw", + 64 => "csltl", + _ => throw new ArgumentOutOfRangeException() + }, + IntTypeNode { Signed: false } intType => intType.Width switch + { + <= 32 => "cultw", + 64 => "cultl", + _ => throw new ArgumentOutOfRangeException() + }, + FloatTypeNode floatType => floatType.Width switch + { + 32 => "clts", + 64 => "cltd", + _ => throw new ArgumentOutOfRangeException() + }, + _ => throw new NotSupportedException($"Less than comparison not supported for type '{type}'") + }, + BinaryOperator.LessThanOrEqual => type switch + { + IntTypeNode { Signed: true } intType => intType.Width switch + { + <= 32 => "cslew", + 64 => "cslel", + _ => throw new ArgumentOutOfRangeException() + }, + IntTypeNode { Signed: false } intType => intType.Width switch + { + <= 32 => "culew", + 64 => "culel", + _ => throw new ArgumentOutOfRangeException() + }, + FloatTypeNode floatType => floatType.Width switch + { + 32 => "cles", + 64 => "cled", + _ => throw new ArgumentOutOfRangeException() + }, + _ => throw new NotSupportedException($"Less than or equal comparison not supported for type '{type}'") + }, + BinaryOperator.GreaterThan => type switch + { + IntTypeNode { Signed: true } intType => intType.Width switch + { + <= 32 => "csgtw", + 64 => "csgtl", + _ => throw new ArgumentOutOfRangeException() + }, + IntTypeNode { Signed: false } intType => intType.Width switch + { + <= 32 => "cugtw", + 64 => "cugtl", + _ => throw new ArgumentOutOfRangeException() + }, + FloatTypeNode floatType => floatType.Width switch + { + 32 => "cgts", + 64 => "cgtd", + _ => throw new ArgumentOutOfRangeException() + }, + _ => throw new NotSupportedException($"Greater than comparison not supported for type '{type}'") + }, + BinaryOperator.GreaterThanOrEqual => type switch + { + IntTypeNode { Signed: true } intType => intType.Width switch + { + <= 32 => "csgew", + 64 => "csgel", + _ => throw new ArgumentOutOfRangeException() + }, + IntTypeNode { Signed: false } intType => intType.Width switch + { + <= 32 => "cugew", + 64 => "cugel", + _ => throw new ArgumentOutOfRangeException() + }, + FloatTypeNode floatType => floatType.Width switch + { + 32 => "cges", + 64 => "cged", + _ => throw new ArgumentOutOfRangeException() + }, + _ => throw new NotSupportedException($"Greater than or equal comparison not supported for type '{type}'") + }, _ => throw new ArgumentOutOfRangeException(nameof(op)) }; } diff --git a/src/compiler/NubLang/Parsing/Parser.cs b/src/compiler/NubLang/Parsing/Parser.cs index 28763e9..486f277 100644 --- a/src/compiler/NubLang/Parsing/Parser.cs +++ b/src/compiler/NubLang/Parsing/Parser.cs @@ -348,16 +348,28 @@ public sealed class Parser { return operatorSyntax switch { - BinaryOperatorSyntax.Multiply => 3, - BinaryOperatorSyntax.Divide => 3, - BinaryOperatorSyntax.Plus => 2, - BinaryOperatorSyntax.Minus => 2, - BinaryOperatorSyntax.GreaterThan => 1, - BinaryOperatorSyntax.GreaterThanOrEqual => 1, - BinaryOperatorSyntax.LessThan => 1, - BinaryOperatorSyntax.LessThanOrEqual => 1, - BinaryOperatorSyntax.Equal => 0, - BinaryOperatorSyntax.NotEqual => 0, + BinaryOperatorSyntax.Multiply => 6, + BinaryOperatorSyntax.Divide => 6, + BinaryOperatorSyntax.Modulo => 6, + + BinaryOperatorSyntax.Plus => 5, + BinaryOperatorSyntax.Minus => 5, + + BinaryOperatorSyntax.LeftShift => 4, + BinaryOperatorSyntax.RightShift => 4, + + BinaryOperatorSyntax.GreaterThan => 3, + BinaryOperatorSyntax.GreaterThanOrEqual => 3, + BinaryOperatorSyntax.LessThan => 3, + BinaryOperatorSyntax.LessThanOrEqual => 3, + + BinaryOperatorSyntax.Equal => 2, + BinaryOperatorSyntax.NotEqual => 2, + + BinaryOperatorSyntax.BitwiseAnd => 1, + BinaryOperatorSyntax.BitwiseXor => 0, + BinaryOperatorSyntax.BitwiseOr => -1, + _ => throw new ArgumentOutOfRangeException(nameof(operatorSyntax), operatorSyntax, null) }; } @@ -396,6 +408,24 @@ public sealed class Parser case Symbol.ForwardSlash: binaryExpressionOperator = BinaryOperatorSyntax.Divide; return true; + case Symbol.Percent: + binaryExpressionOperator = BinaryOperatorSyntax.Modulo; + return true; + case Symbol.LeftShift: + binaryExpressionOperator = BinaryOperatorSyntax.LeftShift; + return true; + case Symbol.RightShift: + binaryExpressionOperator = BinaryOperatorSyntax.RightShift; + return true; + case Symbol.Ampersand: + binaryExpressionOperator = BinaryOperatorSyntax.BitwiseAnd; + return true; + case Symbol.Pipe: + binaryExpressionOperator = BinaryOperatorSyntax.BitwiseOr; + return true; + case Symbol.Caret: + binaryExpressionOperator = BinaryOperatorSyntax.BitwiseXor; + return true; default: binaryExpressionOperator = null; return false; diff --git a/src/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs b/src/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs index 57fb43b..077aba2 100644 --- a/src/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs +++ b/src/compiler/NubLang/Parsing/Syntax/ExpressionSyntax.cs @@ -19,14 +19,20 @@ public enum BinaryOperatorSyntax Plus, Minus, Multiply, - Divide + Divide, + Modulo, + LeftShift, + RightShift, + BitwiseAnd, + BitwiseXor, + BitwiseOr } public abstract record ExpressionSyntax(IEnumerable Tokens) : SyntaxNode(Tokens); -public record BinaryExpressionSyntax(IEnumerable Tokens, ExpressionSyntax Left, BinaryOperatorSyntax OperatorSyntax, ExpressionSyntax Right) : ExpressionSyntax(Tokens); +public record BinaryExpressionSyntax(IEnumerable Tokens, ExpressionSyntax Left, BinaryOperatorSyntax Operator, ExpressionSyntax Right) : ExpressionSyntax(Tokens); -public record UnaryExpressionSyntax(IEnumerable Tokens, UnaryOperatorSyntax OperatorSyntax, ExpressionSyntax Operand) : ExpressionSyntax(Tokens); +public record UnaryExpressionSyntax(IEnumerable Tokens, UnaryOperatorSyntax Operator, ExpressionSyntax Operand) : ExpressionSyntax(Tokens); public record FuncCallSyntax(IEnumerable Tokens, ExpressionSyntax Expression, IReadOnlyList Parameters) : ExpressionSyntax(Tokens); diff --git a/src/compiler/NubLang/Tokenization/Token.cs b/src/compiler/NubLang/Tokenization/Token.cs index 13e9648..dacd560 100644 --- a/src/compiler/NubLang/Tokenization/Token.cs +++ b/src/compiler/NubLang/Tokenization/Token.cs @@ -70,4 +70,8 @@ public enum Symbol For, Extern, Semi, + Percent, + LeftShift, + RightShift, + Pipe, } \ No newline at end of file diff --git a/src/compiler/NubLang/Tokenization/Tokenizer.cs b/src/compiler/NubLang/Tokenization/Tokenizer.cs index 021b4bd..9cb1f2d 100644 --- a/src/compiler/NubLang/Tokenization/Tokenizer.cs +++ b/src/compiler/NubLang/Tokenization/Tokenizer.cs @@ -28,6 +28,8 @@ public sealed class Tokenizer [['!', '=']] = Symbol.NotEqual, [['<', '=']] = Symbol.LessThanOrEqual, [['>', '=']] = Symbol.GreaterThanOrEqual, + [['<', '<']] = Symbol.LeftShift, + [['>', '>']] = Symbol.RightShift, [[':']] = Symbol.Colon, [['(']] = Symbol.OpenParen, [[')']] = Symbol.CloseParen, @@ -48,6 +50,8 @@ public sealed class Tokenizer [['^']] = Symbol.Caret, [['&']] = Symbol.Ampersand, [[';']] = Symbol.Semi, + [['%']] = Symbol.Percent, + [['|']] = Symbol.Pipe, }; private static readonly (char[] Pattern, Symbol Symbol)[] OrderedSymbols = Symbols diff --git a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs index c921431..4d214c7 100644 --- a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs +++ b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs @@ -19,7 +19,13 @@ public enum BinaryOperator Plus, Minus, Multiply, - Divide + Divide, + Modulo, + LeftShift, + RightShift, + BitwiseAnd, + BitwiseXor, + BitwiseOr } public abstract record ExpressionNode(TypeNode Type) : Node; diff --git a/src/compiler/NubLang/TypeChecking/TypeChecker.cs b/src/compiler/NubLang/TypeChecking/TypeChecker.cs index db56ab7..ce3f8f3 100644 --- a/src/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/src/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -345,7 +345,50 @@ public sealed class TypeChecker { var boundLeft = CheckExpression(expression.Left); var boundRight = CheckExpression(expression.Right, boundLeft.Type); - return new BinaryExpressionNode(boundLeft.Type, boundLeft, CheckBinaryOperator(expression.OperatorSyntax), boundRight); + + var op = expression.Operator switch + { + BinaryOperatorSyntax.Equal => BinaryOperator.Equal, + BinaryOperatorSyntax.NotEqual => BinaryOperator.NotEqual, + BinaryOperatorSyntax.GreaterThan => BinaryOperator.GreaterThan, + BinaryOperatorSyntax.GreaterThanOrEqual => BinaryOperator.GreaterThanOrEqual, + BinaryOperatorSyntax.LessThan => BinaryOperator.LessThan, + BinaryOperatorSyntax.LessThanOrEqual => BinaryOperator.LessThanOrEqual, + BinaryOperatorSyntax.Plus => BinaryOperator.Plus, + BinaryOperatorSyntax.Minus => BinaryOperator.Minus, + BinaryOperatorSyntax.Multiply => BinaryOperator.Multiply, + BinaryOperatorSyntax.Divide => BinaryOperator.Divide, + BinaryOperatorSyntax.Modulo => BinaryOperator.Modulo, + BinaryOperatorSyntax.LeftShift => BinaryOperator.LeftShift, + BinaryOperatorSyntax.RightShift => BinaryOperator.RightShift, + BinaryOperatorSyntax.BitwiseAnd => BinaryOperator.BitwiseAnd, + BinaryOperatorSyntax.BitwiseXor => BinaryOperator.BitwiseXor, + BinaryOperatorSyntax.BitwiseOr => BinaryOperator.BitwiseOr, + _ => throw new ArgumentOutOfRangeException(nameof(expression.Operator), expression.Operator, null) + }; + + var resultingType = op switch + { + BinaryOperator.Equal => new BoolTypeNode(), + BinaryOperator.NotEqual => new BoolTypeNode(), + BinaryOperator.GreaterThan => new BoolTypeNode(), + BinaryOperator.GreaterThanOrEqual => new BoolTypeNode(), + BinaryOperator.LessThan => new BoolTypeNode(), + BinaryOperator.LessThanOrEqual => new BoolTypeNode(), + BinaryOperator.Plus => boundLeft.Type, + BinaryOperator.Minus => boundLeft.Type, + BinaryOperator.Multiply => boundLeft.Type, + BinaryOperator.Divide => boundLeft.Type, + BinaryOperator.Modulo => boundLeft.Type, + BinaryOperator.LeftShift => boundLeft.Type, + BinaryOperator.RightShift => boundLeft.Type, + BinaryOperator.BitwiseAnd => boundLeft.Type, + BinaryOperator.BitwiseXor => boundLeft.Type, + BinaryOperator.BitwiseOr => boundLeft.Type, + _ => throw new ArgumentOutOfRangeException() + }; + + return new BinaryExpressionNode(resultingType, boundLeft, op, boundRight); } private DereferenceNode CheckDereference(DereferenceSyntax expression) @@ -606,7 +649,7 @@ public sealed class TypeChecker TypeNode? type = null; - switch (expression.OperatorSyntax) + switch (expression.Operator) { case UnaryOperatorSyntax.Negate: { @@ -633,7 +676,14 @@ public sealed class TypeChecker throw new TypeCheckerException(Diagnostic.Error($"Cannot perform unary operation {expression.Operand} on type {boundOperand.Type}").Build()); } - return new UnaryExpressionNode(type, CheckUnaryOperator(expression.OperatorSyntax), boundOperand); + var op = expression.Operator switch + { + UnaryOperatorSyntax.Negate => UnaryOperator.Negate, + UnaryOperatorSyntax.Invert => UnaryOperator.Invert, + _ => throw new ArgumentOutOfRangeException(nameof(expression.Operator), expression.Operator, null) + }; + + return new UnaryExpressionNode(type, op, boundOperand); } private FuncSignatureNode CheckFuncSignature(FuncSignatureSyntax node) @@ -648,34 +698,6 @@ public sealed class TypeChecker return new FuncSignatureNode(parameters, CheckType(node.ReturnType)); } - private BinaryOperator CheckBinaryOperator(BinaryOperatorSyntax op) - { - return op switch - { - BinaryOperatorSyntax.Equal => BinaryOperator.Equal, - BinaryOperatorSyntax.NotEqual => BinaryOperator.NotEqual, - BinaryOperatorSyntax.GreaterThan => BinaryOperator.GreaterThan, - BinaryOperatorSyntax.GreaterThanOrEqual => BinaryOperator.GreaterThanOrEqual, - BinaryOperatorSyntax.LessThan => BinaryOperator.LessThan, - BinaryOperatorSyntax.LessThanOrEqual => BinaryOperator.LessThanOrEqual, - BinaryOperatorSyntax.Plus => BinaryOperator.Plus, - BinaryOperatorSyntax.Minus => BinaryOperator.Minus, - BinaryOperatorSyntax.Multiply => BinaryOperator.Multiply, - BinaryOperatorSyntax.Divide => BinaryOperator.Divide, - _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) - }; - } - - private UnaryOperator CheckUnaryOperator(UnaryOperatorSyntax op) - { - return op switch - { - UnaryOperatorSyntax.Negate => UnaryOperator.Negate, - UnaryOperatorSyntax.Invert => UnaryOperator.Invert, - _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) - }; - } - private BlockNode CheckBlock(BlockSyntax node, Scope? scope = null) { var statements = new List();