Implement wrapping arithmetic

This commit is contained in:
David Gonzalez Martin 2024-04-22 22:54:42 -06:00
parent 04a1682f57
commit 343b909829
6 changed files with 216 additions and 38 deletions

View File

@ -3805,10 +3805,16 @@ pub const Instruction = union(enum) {
const Id = enum { const Id = enum {
add, add,
wrapping_add,
saturated_add,
sub,
wrapping_sub,
saturated_sub,
mul,
wrapping_mul,
saturated_mul,
div, div,
mod, mod,
mul,
sub,
bit_and, bit_and,
bit_or, bit_or,
bit_xor, bit_xor,
@ -4380,8 +4386,14 @@ pub const IntrinsicId = enum {
pub const ArithmeticLogicIntegerInstruction = enum { pub const ArithmeticLogicIntegerInstruction = enum {
add, add,
wrapping_add,
saturated_add,
sub, sub,
wrapping_sub,
saturated_sub,
mul, mul,
wrapping_mul,
saturated_mul,
div, div,
mod, mod,
bit_and, bit_and,
@ -6728,7 +6740,7 @@ pub const Builder = struct {
fn resolveAssignment(builder: *Builder, unit: *Unit, context: *const Context, node_index: Node.Index) !V { fn resolveAssignment(builder: *Builder, unit: *Unit, context: *const Context, node_index: Node.Index) !V {
const node = unit.getNode(node_index); const node = unit.getNode(node_index);
switch (node.id) { switch (node.id) {
.assign, .add_assign, .sub_assign, .div_assign => { .assign, .add_assign, .sub_assign, .div_assign, .or_assign => {
if (unit.getNode(node.left).id == .discard) { if (unit.getNode(node.left).id == .discard) {
_ = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.right, .right); _ = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.right, .right);
return .{ return .{
@ -6778,6 +6790,7 @@ pub const Builder = struct {
.add_assign => .add, .add_assign => .add,
.sub_assign => .sub, .sub_assign => .sub,
.div_assign => .div, .div_assign => .div,
.or_assign => .bit_or,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}, },
}, },
@ -9992,13 +10005,22 @@ pub const Builder = struct {
}; };
} }
}, },
.add, .sub, .mul, .div, .mod, .bit_and, .bit_or, .bit_xor, .shift_left, .shift_right, .bool_and, .bool_or => block: { .add, .wrapping_add, .saturated_add,
.sub, .wrapping_sub, .saturated_sub,
.mul, .wrapping_mul, .saturated_mul,
.div, .mod, .bit_and, .bit_or, .bit_xor, .shift_left, .shift_right, .bool_and, .bool_or => block: {
const left_node_index = node.left; const left_node_index = node.left;
const right_node_index = node.right; const right_node_index = node.right;
const binary_operation_id: ArithmeticLogicIntegerInstruction = switch (node.id) { const binary_operation_id: ArithmeticLogicIntegerInstruction = switch (node.id) {
.add => .add, .add => .add,
.wrapping_add => .wrapping_add,
.saturated_add => .saturated_add,
.sub => .sub, .sub => .sub,
.wrapping_sub => .wrapping_sub,
.saturated_sub => .saturated_sub,
.mul => .mul, .mul => .mul,
.wrapping_mul => .wrapping_mul,
.saturated_mul => .saturated_mul,
.div => .div, .div => .div,
.mod => .mod, .mod => .mod,
.bit_and => .bit_and, .bit_and => .bit_and,
@ -10026,11 +10048,17 @@ pub const Builder = struct {
}, },
.type => switch (binary_operation_id) { .type => switch (binary_operation_id) {
.add, .add,
.wrapping_add,
.saturated_add,
.sub, .sub,
.wrapping_sub,
.saturated_sub,
.bit_and, .bit_and,
.bit_xor, .bit_xor,
.bit_or, .bit_or,
.mul, .mul,
.wrapping_mul,
.saturated_mul,
.div, .div,
.mod, .mod,
.shift_left, .shift_left,
@ -10070,6 +10098,7 @@ pub const Builder = struct {
.bit_xor => left ^ right, .bit_xor => left ^ right,
.shift_left => left << @as(u6, @intCast(right)), .shift_left => left << @as(u6, @intCast(right)),
.shift_right => left >> @as(u6, @intCast(right)), .shift_right => left >> @as(u6, @intCast(right)),
else => unreachable,
}; };
break :block switch (type_expect) { break :block switch (type_expect) {
@ -10141,8 +10170,14 @@ pub const Builder = struct {
.bit_xor, .bit_xor,
.shift_right, .shift_right,
.add, .add,
.wrapping_add,
.saturated_add,
.sub, .sub,
.wrapping_sub,
.saturated_sub,
.mul, .mul,
.wrapping_mul,
.saturated_mul,
.div, .div,
.mod, .mod,
=> left_value.type, => left_value.type,
@ -10157,10 +10192,16 @@ pub const Builder = struct {
.materialized_int => b: { .materialized_int => b: {
const id: Instruction.IntegerBinaryOperation.Id = switch (binary_operation_id) { const id: Instruction.IntegerBinaryOperation.Id = switch (binary_operation_id) {
.add => .add, .add => .add,
.wrapping_add => .wrapping_add,
.saturated_add => .saturated_add,
.sub => .sub,
.wrapping_sub => .wrapping_sub,
.saturated_sub => .saturated_sub,
.mul => .mul,
.wrapping_mul => .wrapping_mul,
.saturated_mul => .saturated_mul,
.div => .div, .div => .div,
.mod => .mod, .mod => .mod,
.mul => .mul,
.sub => .sub,
.bit_and => .bit_and, .bit_and => .bit_and,
.bit_or => .bit_or, .bit_or => .bit_or,
.bit_xor => .bit_xor, .bit_xor => .bit_xor,
@ -13776,7 +13817,7 @@ pub const Builder = struct {
try builder.insertDebugCheckPoint(unit, context, statement_node.token); try builder.insertDebugCheckPoint(unit, context, statement_node.token);
switch (statement_node.id) { switch (statement_node.id) {
.assign, .add_assign, .sub_assign, .div_assign => { .assign, .add_assign, .sub_assign, .div_assign, .or_assign => {
_ = try builder.resolveAssignment(unit, context, statement_node_index); _ = try builder.resolveAssignment(unit, context, statement_node_index);
}, },
.if_else => { .if_else => {
@ -17349,8 +17390,14 @@ pub const Token = struct {
// Binary // Binary
operator_assign, operator_assign,
operator_add, operator_add,
operator_saturated_add,
operator_wrapping_add,
operator_minus, operator_minus,
operator_saturated_sub,
operator_wrapping_sub,
operator_asterisk, operator_asterisk,
operator_saturated_mul,
operator_wrapping_mul,
operator_div, operator_div,
operator_mod, operator_mod,
operator_bar, operator_bar,
@ -17359,8 +17406,14 @@ pub const Token = struct {
operator_shift_left, operator_shift_left,
operator_shift_right, operator_shift_right,
operator_add_assign, operator_add_assign,
operator_wrapping_add_assign,
operator_saturated_add_assign,
operator_sub_assign, operator_sub_assign,
operator_wrapping_sub_assign,
operator_saturated_sub_assign,
operator_mul_assign, operator_mul_assign,
operator_wrapping_mul_assign,
operator_saturated_mul_assign,
operator_div_assign, operator_div_assign,
operator_mod_assign, operator_mod_assign,
operator_or_assign, operator_or_assign,

View File

@ -2762,14 +2762,20 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
const left = try llvm.emitRightValue(unit, context, binary_operation.left); const left = try llvm.emitRightValue(unit, context, binary_operation.left);
const right = try llvm.emitRightValue(unit, context, binary_operation.right); const right = try llvm.emitRightValue(unit, context, binary_operation.right);
assert(left.getType() == right.getType()); assert(left.getType() == right.getType());
const no_signed_wrapping = binary_operation.signedness == .signed; const no_signed_wrapping = binary_operation.signedness == .signed or switch (binary_operation.id) {
const no_unsigned_wrapping = binary_operation.signedness == .unsigned; .wrapping_add, .wrapping_sub, .wrapping_mul => false,
else => true,
};
const no_unsigned_wrapping = binary_operation.signedness == .unsigned or switch (binary_operation.id) {
.wrapping_add, .wrapping_sub, .wrapping_mul => false,
else => true,
};
const name = @tagName(binary_operation.id); const name = @tagName(binary_operation.id);
const is_exact = false; const is_exact = false;
const instruction = switch (binary_operation.id) { const instruction = switch (binary_operation.id) {
.add => llvm.builder.createAdd(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add, .add, .wrapping_add => llvm.builder.createAdd(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add,
.mul => llvm.builder.createMultiply(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.multiply, .sub, .wrapping_sub => llvm.builder.createSub(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add,
.sub => llvm.builder.createSub(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add, .mul, .wrapping_mul => llvm.builder.createMultiply(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.multiply,
.div => switch (binary_operation.signedness) { .div => switch (binary_operation.signedness) {
.unsigned => llvm.builder.createUDiv(left, right, name.ptr, name.len, is_exact) orelse unreachable, .unsigned => llvm.builder.createUDiv(left, right, name.ptr, name.len, is_exact) orelse unreachable,
.signed => llvm.builder.createSDiv(left, right, name.ptr, name.len, is_exact) orelse unreachable, .signed => llvm.builder.createSDiv(left, right, name.ptr, name.len, is_exact) orelse unreachable,
@ -2786,7 +2792,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
.unsigned => llvm.builder.createLogicalShiftRight(left, right, name.ptr, name.len, is_exact) orelse unreachable, .unsigned => llvm.builder.createLogicalShiftRight(left, right, name.ptr, name.len, is_exact) orelse unreachable,
.signed => llvm.builder.createArithmeticShiftRight(left, right, name.ptr, name.len, is_exact) orelse unreachable, .signed => llvm.builder.createArithmeticShiftRight(left, right, name.ptr, name.len, is_exact) orelse unreachable,
}, },
//else => |t| @panic(@tagName(t)), else => unreachable,
}; };
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, instruction); try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, instruction);
}, },

View File

@ -313,6 +313,26 @@ pub fn analyze(allocator: *MyAllocator, text: []const u8, token_buffer: *Token.B
index += 1; index += 1;
break :b .operator_add_assign; break :b .operator_add_assign;
}, },
'|' => b: {
index += 1;
break :b switch (text[index]) {
'=' => assign: {
index += 1;
break :assign .operator_saturated_add_assign;
},
else => .operator_saturated_add,
};
},
'%' => b: {
index += 1;
break :b switch (text[index]) {
'=' => assign: {
index += 1;
break :assign .operator_wrapping_add_assign;
},
else => .operator_wrapping_add,
};
},
else => .operator_add, else => .operator_add,
}; };
@ -325,6 +345,26 @@ pub fn analyze(allocator: *MyAllocator, text: []const u8, token_buffer: *Token.B
index += 1; index += 1;
break :b .operator_sub_assign; break :b .operator_sub_assign;
}, },
'|' => b: {
index += 1;
break :b switch (text[index]) {
'=' => assign: {
index += 1;
break :assign .operator_saturated_add_assign;
},
else => .operator_saturated_sub,
};
},
'%' => b: {
index += 1;
break :b switch (text[index]) {
'=' => assign: {
index += 1;
break :assign .operator_wrapping_sub_assign;
},
else => .operator_wrapping_sub,
};
},
else => .operator_minus, else => .operator_minus,
}; };
@ -337,6 +377,26 @@ pub fn analyze(allocator: *MyAllocator, text: []const u8, token_buffer: *Token.B
index += 1; index += 1;
break :b .operator_mul_assign; break :b .operator_mul_assign;
}, },
'|' => b: {
index += 1;
break :b switch (text[index]) {
'=' => assign: {
index += 1;
break :assign .operator_saturated_mul_assign;
},
else => .operator_saturated_mul,
};
},
'%' => b: {
index += 1;
break :b switch (text[index]) {
'=' => assign: {
index += 1;
break :assign .operator_wrapping_mul_assign;
},
else => .operator_wrapping_mul,
};
},
else => .operator_asterisk, else => .operator_asterisk,
}; };

View File

@ -206,6 +206,13 @@ pub const Node = struct {
slice_metadata, slice_metadata,
orelse_expression, orelse_expression,
type, type,
or_assign,
wrapping_add,
saturated_add,
wrapping_sub,
saturated_sub,
wrapping_mul,
saturated_mul,
}; };
}; };
@ -871,6 +878,7 @@ const Analyzer = struct {
.operator_mul_assign => .mul_assign, .operator_mul_assign => .mul_assign,
.operator_div_assign => .div_assign, .operator_div_assign => .div_assign,
.operator_mod_assign => .mod_assign, .operator_mod_assign => .mod_assign,
.operator_or_assign => .or_assign,
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
analyzer.consumeToken(); analyzer.consumeToken();
@ -1052,8 +1060,14 @@ const Analyzer = struct {
compare_less_equal, compare_less_equal,
compare_greater_equal, compare_greater_equal,
add, add,
wrapping_add,
saturated_add,
sub, sub,
wrapping_sub,
saturated_sub,
mul, mul,
wrapping_mul,
saturated_mul,
div, div,
mod, mod,
bit_and, bit_and,
@ -1075,8 +1089,14 @@ const Analyzer = struct {
.compare_less_equal = 30, .compare_less_equal = 30,
.compare_greater_equal = 30, .compare_greater_equal = 30,
.add = 60, .add = 60,
.wrapping_add = 60,
.saturated_add = 60,
.sub = 60, .sub = 60,
.wrapping_sub = 60,
.saturated_sub = 60,
.mul = 70, .mul = 70,
.wrapping_mul = 70,
.saturated_mul = 70,
.div = 70, .div = 70,
.mod = 70, .mod = 70,
.bit_and = 40, .bit_and = 40,
@ -1098,13 +1118,19 @@ const Analyzer = struct {
.compare_less_equal = .none, .compare_less_equal = .none,
.compare_greater_equal = .none, .compare_greater_equal = .none,
.add = .left, .add = .left,
.wrapping_add = .left,
.saturated_add = .left,
.sub = .left, .sub = .left,
.wrapping_sub = .left,
.saturated_sub = .left,
.bit_and = .left, .bit_and = .left,
.bit_xor = .left, .bit_xor = .left,
.bit_or = .left, .bit_or = .left,
.bool_and = .left, .bool_and = .left,
.bool_or = .left, .bool_or = .left,
.mul = .left, .mul = .left,
.wrapping_mul = .left,
.saturated_mul = .left,
.div = .left, .div = .left,
.mod = .left, .mod = .left,
.shift_left = .left, .shift_left = .left,
@ -1121,13 +1147,19 @@ const Analyzer = struct {
.compare_greater_equal = .compare_greater_equal, .compare_greater_equal = .compare_greater_equal,
.compare_less_equal = .compare_less_equal, .compare_less_equal = .compare_less_equal,
.add = .add, .add = .add,
.wrapping_add = .wrapping_add,
.saturated_add = .saturated_add,
.sub = .sub, .sub = .sub,
.wrapping_sub = .wrapping_sub,
.saturated_sub = .saturated_sub,
.bit_and = .bit_and, .bit_and = .bit_and,
.bit_xor = .bit_xor, .bit_xor = .bit_xor,
.bit_or = .bit_or, .bit_or = .bit_or,
.bool_and = .bool_and, .bool_and = .bool_and,
.bool_or = .bool_or, .bool_or = .bool_or,
.mul = .mul, .mul = .mul,
.wrapping_mul = .wrapping_mul,
.saturated_mul = .saturated_mul,
.div = .div, .div = .div,
.mod = .mod, .mod = .mod,
.shift_left = .shift_left, .shift_left = .shift_left,
@ -1162,6 +1194,7 @@ const Analyzer = struct {
.operator_mul_assign, .operator_mul_assign,
.operator_div_assign, .operator_div_assign,
.operator_mod_assign, .operator_mod_assign,
.operator_or_assign,
.operator_dot, .operator_dot,
.operator_double_dot, .operator_double_dot,
.operator_triple_dot, .operator_triple_dot,
@ -1183,8 +1216,14 @@ const Analyzer = struct {
.operator_compare_less_equal => .compare_less_equal, .operator_compare_less_equal => .compare_less_equal,
.operator_compare_greater_equal => .compare_greater_equal, .operator_compare_greater_equal => .compare_greater_equal,
.operator_add => .add, .operator_add => .add,
.operator_wrapping_add => .wrapping_add,
.operator_saturated_add => .saturated_add,
.operator_minus => .sub, .operator_minus => .sub,
.operator_wrapping_sub => .wrapping_sub,
.operator_saturated_sub => .saturated_sub,
.operator_asterisk => .mul, .operator_asterisk => .mul,
.operator_wrapping_mul => .wrapping_mul,
.operator_saturated_mul => .saturated_mul,
.operator_div => .div, .operator_div => .div,
.operator_mod => .mod, .operator_mod => .mod,
.operator_ampersand => .bit_and, .operator_ampersand => .bit_and,

View File

@ -6,6 +6,39 @@ const print = std.print;
const print_usize = std.print_usize; const print_usize = std.print_usize;
const exit = std.os.exit; const exit = std.os.exit;
const lex = fn (arena: &Arena, bytes: []const u8) *!void {
if (bytes.length >= 0xffffffff) {
unreachable;
}
const length: u32 = #cast(bytes.length);
const max_initial_keyword_len: u32 = 8;
//var index: u32 = 0;
//while (index + 64 < length) {
// var i = index;
// const top = index + max_initial_keyword_len + 1;
// var space: u32 = 0;
// while (i < top) {
// const is_space = bytes[i] == ' ';
// space |= #cast(
// i += 1;
// }
// if (space == 0) {
// unreachable;
// }
//}
}
const FileStartToken = enum{
"comptime",
"test",
"const",
"var",
};
const ArgumentProcessingError = error{ const ArgumentProcessingError = error{
no_arguments, no_arguments,
}; };
@ -58,31 +91,6 @@ const FixedKeyword = enum{
"continue", "continue",
}; };
const FileStartToken = enum{
"comptime",
"test",
"const",
"var",
};
const lex = fn (arena: &Arena, bytes: []const u8) *!void {
if (bytes.length >= 0xffffffff) {
unreachable;
}
const length: u32 = #cast(bytes.length);
var i: u32 = 0;
//var index: u32 = 0;
//while (index + 64 < length) {
// var i = index;
// const top = index + 64;
// while (i < top) {
// i += 1;
// }
//}
}
const get_argument = fn (real_argument: []const u8, wanted_argument: []const u8, command_arguments: []const [&:0]const u8, i_ptr: &usize) ?[]const u8 { const get_argument = fn (real_argument: []const u8, wanted_argument: []const u8, command_arguments: []const [&:0]const u8, i_ptr: &usize) ?[]const u8 {
const i = i_ptr.@; const i = i_ptr.@;

View File

@ -0,0 +1,12 @@
const std = #import("std");
const expect = std.testing.expect;
const main = fn () *!void {
var a: u8 = 255;
var b: u8 = 1;
const result = a +% b;
try expect(result == 0);
var c: u16 = 0;
var d: u16 = 1;
const sub_result = c -% d;
try expect(sub_result == 65535);
}