From 381c9c76af756f6aaad29dd531ed2459d35c94fa Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 17 Dec 2021 14:55:01 +0200 Subject: [PATCH] Value: extract constant folding into its own file --- .github/workflows/ci.yml | 3 + src/Diagnostics.zig | 4 +- src/Parser.zig | 426 ++++++++++++--------------------------- src/Tree.zig | 12 +- src/Value.zig | 382 +++++++++++++++++++++++++++++++++++ src/codegen/x86_64.zig | 18 +- 6 files changed, 528 insertions(+), 317 deletions(-) create mode 100644 src/Value.zig diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7ea414e..fde14b2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,9 @@ name: CI on: push: + branches: + - master + - tmp pull_request: paths: - '**.zig' diff --git a/src/Diagnostics.zig b/src/Diagnostics.zig index 5adbdcdb..f8a2c51f 100644 --- a/src/Diagnostics.zig +++ b/src/Diagnostics.zig @@ -31,8 +31,8 @@ pub const Message = struct { resembles: u21, }, actual_codepoint: u21, - unsigned: u64, - signed: i64, + unsigned: u128, + signed: i128, none: void, }; }; diff --git a/src/Parser.zig b/src/Parser.zig index 0db8097d..ef6734e9 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -16,6 +16,7 @@ const NodeList = std.ArrayList(NodeIndex); const InitList = @import("InitList.zig"); const Attribute = @import("Attribute.zig"); const CharInfo = @import("CharInfo.zig"); +const Value = @import("Value.zig"); const Parser = @This(); @@ -50,10 +51,10 @@ const Scope = union(enum) { const ResultContext = struct { pub fn eql(_: ResultContext, a: Result, b: Result) bool { - return a.as_u64() == b.as_u64(); + return a.val.compare(.eq, b.val); } pub fn hash(_: ResultContext, a: Result) u64 { - return a.hash(); + return a.val.hash(); } }; const CaseMap = std.HashMap(Result, Case, ResultContext, std.hash_map.default_max_load_percentage); @@ -272,6 +273,14 @@ fn expectClosing(p: *Parser, opening: TokenIndex, id: Token.Id) Error!void { }; } +fn errOverflow(p: *Parser, op_tok: TokenIndex, res: Result) !void { + if (res.ty.isUnsignedInt(p.pp.comp)) { + try p.errExtra(.overflow_unsigned, op_tok, .{ .unsigned = res.val.getInt(u128) }); + } else { + try p.errExtra(.overflow_signed, op_tok, .{ .signed = res.val.getInt(i128) }); + } +} + fn errExpectedToken(p: *Parser, expected: Token.Id, actual: Token.Id) Error { switch (actual) { .invalid => try p.errExtra(.expected_invalid, p.tok_i, .{ .tok_id_expected = expected }), @@ -993,12 +1002,12 @@ fn staticAssert(p: *Parser) Error!bool { _ = try p.expectToken(.semicolon); if (str.node == .none) try p.errTok(.static_assert_missing_message, static_assert); - if (res.val == .unavailable) { + if (res.val.tag == .unavailable) { // an unavailable sizeof expression is already a compile error, so we don't emit // another error for an invalid _Static_assert condition. This matches the behavior // of gcc/clang if (!p.nodeIs(res.node, .sizeof_expr)) try p.errTok(.static_assert_not_constant, res_token); - } else if (!res.getBool()) { + } else if (!res.val.getBool()) { if (str.node != .none) { var buf = std.ArrayList(u8).init(p.pp.comp.gpa); defer buf.deinit(); @@ -1562,15 +1571,15 @@ fn typeSpec(p: *Parser, ty: *Type.Builder) Error!bool { ty.alignment = inner_ty.alignment; } else blk: { const res = try p.constExpr(); - if (res.val == .unavailable) { + if (res.val.tag == .unavailable) { try p.errTok(.alignas_unavailable, ty.align_tok.?); break :blk; - } else if (res.val == .signed and res.val.signed < 0) { - try p.errExtra(.negative_alignment, ty.align_tok.?, .{ .signed = res.val.signed }); + } else if (res.val.compare(.lt, Value.zero)) { + try p.errExtra(.negative_alignment, ty.align_tok.?, .{ .signed = res.val.getInt(i128) }); break :blk; } - var requested = std.math.cast(u29, res.as_u64()) catch { - try p.errExtra(.maximum_alignment, ty.align_tok.?, .{ .unsigned = res.as_u64() }); + var requested = std.math.cast(u29, res.val.data.int) catch { + try p.errExtra(.maximum_alignment, ty.align_tok.?, .{ .unsigned = res.val.getInt(u128) }); break :blk; }; if (requested == 0) { @@ -1791,24 +1800,23 @@ fn recordDeclarator(p: *Parser) Error!bool { break :bits; } - if (res.val == .unavailable) { + if (res.val.tag == .unavailable) { try p.errTok(.expected_integer_constant_expr, first_tok); break :bits; - } else if (res.val == .signed and res.val.signed < 0) { - try p.errExtra(.negative_bitwidth, first_tok, .{ .signed = res.val.signed }); + } else if (res.val.compare(.lt, Value.zero)) { + try p.errExtra(.negative_bitwidth, first_tok, .{ .signed = res.val.getInt(i128) }); break :bits; } - const width = res.as_u64(); - if (width == 0 and name_tok != 0) { - try p.errTok(.zero_width_named_field, name_tok); - break :bits; - } else if (width > ty.bitSizeof(p.pp.comp).?) { + if (res.val.compare(.gt, Value.int(ty.bitSizeof(p.pp.comp).?))) { try p.errTok(.bitfield_too_big, name_tok); break :bits; + } else if (res.val.isZero() and name_tok != 0) { + try p.errTok(.zero_width_named_field, name_tok); + break :bits; } - bits = @truncate(u32, width); + bits = res.val.getInt(u32); bits_node = res.node; } @@ -1988,7 +1996,6 @@ const Enumerator = struct { fn init(p: *Parser) Enumerator { return .{ .res = .{ .ty = .{ .specifier = if (p.pp.comp.langopts.short_enums) .schar else .int }, - .val = .{ .signed = 0 }, } }; } @@ -1996,11 +2003,7 @@ const Enumerator = struct { fn incr(e: *Enumerator, p: *Parser) !void { e.res.node = .none; _ = p; - switch (e.res.val) { - .unavailable => unreachable, - .signed => |*v| v.* += 1, - .unsigned => |*v| v.* += 1, - } + _ = e.res.val.add(e.res.val, Value.one, e.res.ty, p.pp.comp); // TODO adjust type if value does not fit current } @@ -2030,7 +2033,7 @@ fn enumerator(p: *Parser, e: *Enumerator) Error!?EnumFieldAndNode { if (p.eatToken(.equal)) |_| { const specified = try p.constExpr(); - if (specified.val == .unavailable) { + if (specified.val.tag == .unavailable) { try p.errTok(.enum_val_unavailable, name_tok + 2); try e.incr(p); } else { @@ -2217,8 +2220,8 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato const outer_size = if (outer.hasIncompleteSize()) 1 else outer.sizeof(p.pp.comp); const max_elems = max_bytes / std.math.max(1, outer_size orelse 1); - switch (size.val) { - .unavailable => if (size.node != .none) { + if (size.val.tag == .unavailable) { + if (size.node != .none) { if (p.func.ty == null and kind != .param and p.record.kind == .invalid) { try p.errTok(.variable_len_array_file_scope, l_bracket); } @@ -2237,28 +2240,20 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato arr_ty.len = 0; res_ty.data = .{ .array = arr_ty }; res_ty.specifier = .incomplete_array; - }, - .unsigned => |v| { - const arr_ty = try p.arena.create(Type.Array); - arr_ty.len = v; - if (arr_ty.len > max_elems) { - try p.errTok(.array_too_large, l_bracket); - arr_ty.len = max_elems; - } - res_ty.data = .{ .array = arr_ty }; - res_ty.specifier = .array; - }, - .signed => |v| { - if (v < 0) try p.errTok(.negative_array_size, l_bracket); - const arr_ty = try p.arena.create(Type.Array); - arr_ty.len = @bitCast(u64, v); - if (arr_ty.len > max_elems) { - try p.errTok(.array_too_large, l_bracket); - arr_ty.len = max_elems; - } - res_ty.data = .{ .array = arr_ty }; - res_ty.specifier = .array; - }, + } + } else { + if (size.val.compare(.lt, Value.zero)) { + try p.errTok(.negative_array_size, l_bracket); + } + const arr_ty = try p.arena.create(Type.Array); + if (size.val.compare(.gt, Value.int(max_elems))) { + try p.errTok(.array_too_large, l_bracket); + arr_ty.len = max_elems; + } else { + arr_ty.len = size.val.getInt(u64); + } + res_ty.data = .{ .array = arr_ty }; + res_ty.specifier = .array; } try res_ty.combine(outer, p, l_bracket); @@ -2527,23 +2522,20 @@ fn initializerItem(p: *Parser, il: *InitList, init_ty: Type) Error!bool { const index_res = try p.constExpr(); try p.expectClosing(l_bracket, .r_bracket); - const index_unchecked = switch (index_res.val) { - .unsigned => |val| val, - .signed => |val| if (val < 0) { - try p.errExtra(.negative_array_designator, l_bracket + 1, .{ .signed = val }); - return error.ParsingFailed; - } else @intCast(u64, val), - .unavailable => { - try p.errTok(.expected_integer_constant_expr, expr_tok); - return error.ParsingFailed; - }, - }; + if (index_res.val.tag == .unavailable) { + try p.errTok(.expected_integer_constant_expr, expr_tok); + return error.ParsingFailed; + } else if (index_res.val.compare(.lt, Value.zero)) { + try p.errExtra(.negative_array_designator, l_bracket + 1, .{ .signed = index_res.val.getInt(i128) }); + return error.ParsingFailed; + } + const max_len = cur_ty.arrayLen() orelse std.math.maxInt(usize); - if (index_unchecked >= max_len) { - try p.errExtra(.oob_array_designator, l_bracket + 1, .{ .unsigned = index_unchecked }); + if (index_res.val.data.int >= max_len) { + try p.errExtra(.oob_array_designator, l_bracket + 1, .{ .unsigned = index_res.val.data.int }); return error.ParsingFailed; } - const checked = @intCast(usize, index_unchecked); + const checked = index_res.val.getInt(u64); cur_index_hint = cur_index_hint orelse checked; cur_il = try cur_il.find(p.pp.comp.gpa, checked); @@ -2880,7 +2872,7 @@ fn coerceInit(p: *Parser, item: *Result, tok: TokenIndex, target: Type) !void { try p.errStr(.incompatible_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); } } else if (unqual_ty.isPtr()) { - if (item.isZero()) { + if (item.val.isZero()) { try item.nullCast(p, target); } else if (item.ty.isInt()) { try p.errStr(.implicit_int_to_ptr, tok, try p.typePairStrExtra(item.ty, " to ", target)); @@ -3323,7 +3315,7 @@ fn stmt(p: *Parser) Error!NodeIndex { .specifier = .pointer, .data = .{ .sub_type = elem_ty }, }; - if (e.isZero()) { + if (e.val.isZero()) { try e.nullCast(p, result_ty); } else { try p.errStr(.implicit_int_to_ptr, expr_tok, try p.typePairStrExtra(e.ty, " to ", result_ty)); @@ -3434,7 +3426,7 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex { .data = .{ .bin = .{ .lhs = val.node, .rhs = s } }, }); if (p.findSwitch()) |some| { - if (val.val == .unavailable) { + if (val.val.tag == .unavailable) { try p.errTok(.case_val_unavailable, case + 1); return node; } @@ -3721,7 +3713,7 @@ fn returnStmt(p: *Parser) Error!?NodeIndex { try p.errStr(.incompatible_return, e_tok, try p.typeStr(e.ty)); } } else if (ret_ty.isPtr()) { - if (e.isZero()) { + if (e.val.isZero()) { try e.nullCast(p, ret_ty); } else if (e.ty.isInt()) { try p.errStr(.implicit_int_to_ptr, e_tok, try p.typePairStrExtra(e.ty, " to ", ret_ty)); @@ -3749,49 +3741,21 @@ pub fn macroExpr(p: *Parser) Compilation.Error!bool { error.FatalError => return error.FatalError, error.ParsingFailed => return false, }; - if (res.val == .unavailable) { + if (res.val.tag == .unavailable) { try p.errTok(.expected_expr, p.tok_i); return false; } - return res.getBool(); + return res.val.getBool(); } const Result = struct { node: NodeIndex = .none, ty: Type = .{ .specifier = .int }, - val: union(enum) { - unsigned: u64, - signed: i64, - unavailable, - } = .unavailable, - - pub fn getBool(res: Result) bool { - return switch (res.val) { - .signed => |v| v != 0, - .unsigned => |v| v != 0, - .unavailable => unreachable, - }; - } - - fn as_u64(res: Result) u64 { - return switch (res.val) { - .signed => |v| @bitCast(u64, v), - .unsigned => |v| v, - .unavailable => unreachable, - }; - } - - fn isZero(res: Result) bool { - return switch (res.val) { - .signed => |v| v == 0, - .unsigned => |v| v == 0, - .unavailable => false, - }; - } + val: Value = .{}, fn expect(res: Result, p: *Parser) Error!void { if (p.in_macro) { - if (res.val == .unavailable) { + if (res.val.tag == .unavailable) { try p.errTok(.expected_expr, p.tok_i); return error.ParsingFailed; } @@ -3804,7 +3768,7 @@ const Result = struct { } fn empty(res: Result, p: *Parser) bool { - if (p.in_macro) return res.val == .unavailable; + if (p.in_macro) return res.val.tag == .unavailable; return res.node == .none; } @@ -3963,7 +3927,7 @@ const Result = struct { if (!a_scalar or !b_scalar or (a_float and b_ptr) or (b_float and a_ptr)) return a.invalidBinTy(tok, b, p); - if ((a_int or b_int) and !(a.isZero() or b.isZero())) { + if ((a_int or b_int) and !(a.val.isZero() or b.val.isZero())) { try p.errStr(.comparison_ptr_int, tok, try p.typePairStr(a.ty, b.ty)); } else if (a_ptr and b_ptr) { if (!a.ty.isVoidStar() and !b.ty.isVoidStar() and !a.ty.eql(b.ty, false)) @@ -3985,7 +3949,7 @@ const Result = struct { return true; } if ((a_ptr and b_int) or (a_int and b_ptr)) { - if (a.isZero() or b.isZero()) { + if (a.val.isZero() or b.val.isZero()) { try a.nullCast(p, b.ty); try b.nullCast(p, a.ty); return true; @@ -4053,12 +4017,15 @@ const Result = struct { fn boolCast(res: *Result, p: *Parser, bool_ty: Type) Error!void { if (res.ty.isPtr()) { + res.val.intToBool(); res.ty = bool_ty; try res.un(p, .pointer_to_bool); } else if (res.ty.isInt() and !res.ty.is(.bool)) { + res.val.intToBool(); res.ty = bool_ty; try res.un(p, .int_to_bool); } else if (res.ty.isFloat()) { + res.val.floatToInt(res.ty, bool_ty); res.ty = bool_ty; try res.un(p, .float_to_bool); } @@ -4072,28 +4039,23 @@ const Result = struct { res.ty = int_ty; try res.un(p, .pointer_to_int); } else if (res.ty.isFloat()) { + res.val.floatToInt(res.ty, int_ty); res.ty = int_ty; try res.un(p, .float_to_int); } else if (!res.ty.eql(int_ty, true)) { + res.val.intCast(res.ty, int_ty); res.ty = int_ty; try res.un(p, .int_cast); } - - const is_unsigned = int_ty.isUnsignedInt(p.pp.comp); - if (is_unsigned and res.val == .signed) { - const copy = res.val.signed; - res.val = .{ .unsigned = @bitCast(u64, copy) }; - } else if (!is_unsigned and res.val == .unsigned) { - const copy = res.val.unsigned; - res.val = .{ .signed = @bitCast(i64, copy) }; - } } fn floatCast(res: *Result, p: *Parser, float_ty: Type) Error!void { if (res.ty.is(.bool)) { + res.val.intToFloat(res.ty); res.ty = float_ty; try res.un(p, .bool_to_float); } else if (res.ty.isInt()) { + res.val.intToFloat(res.ty); res.ty = float_ty; try res.un(p, .int_to_float); } else if (!res.ty.eql(float_ty, true)) { @@ -4107,6 +4069,7 @@ const Result = struct { res.ty = ptr_ty; try res.un(p, .bool_to_pointer); } else if (res.ty.isInt()) { + res.val.intCast(res.ty, ptr_ty); res.ty = ptr_ty; try res.un(p, .int_to_pointer); } @@ -4124,7 +4087,7 @@ const Result = struct { } fn nullCast(res: *Result, p: *Parser, ptr_ty: Type) Error!void { - if (!res.isZero()) return; + if (!res.val.isZero()) return; res.ty = ptr_ty; try res.un(p, .null_to_pointer); } @@ -4194,7 +4157,7 @@ const Result = struct { fn shouldEval(a: *Result, b: *Result, p: *Parser) Error!bool { if (p.no_eval) return false; - if (a.val != .unavailable and b.val != .unavailable) + if (a.val.tag != .unavailable and b.val.tag != .unavailable) return true; try a.saveValue(p); @@ -4202,142 +4165,12 @@ const Result = struct { return p.no_eval; } + /// Saves value and replaces it with `.unavailable`. fn saveValue(res: *Result, p: *Parser) !void { assert(!p.in_macro); - switch (res.val) { - .unsigned => |v| try p.value_map.put(res.node, v), - .signed => |v| try p.value_map.put(res.node, @bitCast(u64, v)), - .unavailable => return, - } - res.val = .unavailable; - } - - fn hash(res: Result) u64 { - switch (res.val) { - .unsigned => |v| return std.hash.Wyhash.hash(0, mem.asBytes(&v)), - .signed => |v| return std.hash.Wyhash.hash(0, mem.asBytes(&v)), - .unavailable => unreachable, - } - } - - fn eql(a: Result, b: Result) bool { - return a.compare(.eq, b); - } - - fn compare(a: Result, op: std.math.CompareOperator, b: Result) bool { - switch (a.val) { - .unsigned => |val| return std.math.compare(val, op, b.val.unsigned), - .signed => |val| return std.math.compare(val, op, b.val.signed), - .unavailable => unreachable, - } - } - - fn mul(a: *Result, tok: TokenIndex, b: Result, p: *Parser) !void { - const size = a.ty.sizeof(p.pp.comp).?; - var overflow = false; - switch (a.val) { - .unsigned => |*v| { - switch (size) { - 1 => unreachable, // promoted to int - 2 => unreachable, // promoted to int - 4 => { - var res: u32 = undefined; - overflow = @mulWithOverflow(u32, @truncate(u32, v.*), @truncate(u32, b.val.unsigned), &res); - v.* = res; - }, - 8 => overflow = @mulWithOverflow(u64, v.*, b.val.unsigned, v), - else => unreachable, - } - if (overflow) try p.errExtra(.overflow_unsigned, tok, .{ .unsigned = v.* }); - }, - .signed => |*v| { - switch (size) { - 1 => unreachable, // promoted to int - 2 => unreachable, // promoted to int - 4 => { - var res: i32 = undefined; - overflow = @mulWithOverflow(i32, @truncate(i32, v.*), @truncate(i32, b.val.signed), &res); - v.* = res; - }, - 8 => overflow = @mulWithOverflow(i64, v.*, b.val.signed, v), - else => unreachable, - } - if (overflow) try p.errExtra(.overflow_signed, tok, .{ .signed = v.* }); - }, - .unavailable => unreachable, - } - } - - fn add(a: *Result, tok: TokenIndex, b: Result, p: *Parser) !void { - const size = a.ty.sizeof(p.pp.comp).?; - var overflow = false; - switch (a.val) { - .unsigned => |*v| { - switch (size) { - 1 => unreachable, // promoted to int - 2 => unreachable, // promoted to int - 4 => { - var res: u32 = undefined; - overflow = @addWithOverflow(u32, @truncate(u32, v.*), @truncate(u32, b.val.unsigned), &res); - v.* = res; - }, - 8 => overflow = @addWithOverflow(u64, v.*, b.val.unsigned, v), - else => unreachable, - } - if (overflow) try p.errExtra(.overflow_unsigned, tok, .{ .unsigned = v.* }); - }, - .signed => |*v| { - switch (size) { - 1 => unreachable, // promoted to int - 2 => unreachable, // promoted to int - 4 => { - var res: i32 = undefined; - overflow = @addWithOverflow(i32, @truncate(i32, v.*), @truncate(i32, b.val.signed), &res); - v.* = res; - }, - 8 => overflow = @addWithOverflow(i64, v.*, b.val.signed, v), - else => unreachable, - } - if (overflow) try p.errExtra(.overflow_signed, tok, .{ .signed = v.* }); - }, - .unavailable => unreachable, - } - } - - fn sub(a: *Result, tok: TokenIndex, b: Result, p: *Parser) !void { - const size = a.ty.sizeof(p.pp.comp).?; - var overflow = false; - switch (a.val) { - .unsigned => |*v| { - switch (size) { - 1 => unreachable, // promoted to int - 2 => unreachable, // promoted to int - 4 => { - var res: u32 = undefined; - overflow = @subWithOverflow(u32, @truncate(u32, v.*), @truncate(u32, b.val.unsigned), &res); - v.* = res; - }, - 8 => overflow = @subWithOverflow(u64, v.*, b.val.unsigned, v), - else => unreachable, - } - if (overflow) try p.errExtra(.overflow_unsigned, tok, .{ .unsigned = v.* }); - }, - .signed => |*v| { - switch (size) { - 1 => unreachable, // promoted to int - 2 => unreachable, // promoted to int - 4 => { - var res: i32 = undefined; - overflow = @subWithOverflow(i32, @truncate(i32, v.*), @truncate(i32, b.val.signed), &res); - v.* = res; - }, - 8 => overflow = @subWithOverflow(i64, v.*, b.val.signed, v), - else => unreachable, - } - if (overflow) try p.errExtra(.overflow_signed, tok, .{ .signed = v.* }); - }, - .unavailable => unreachable, - } + if (res.val.tag == .unavailable) return; + try p.value_map.put(res.node, res.val); + res.val.tag = .unavailable; } }; @@ -4430,7 +4263,7 @@ fn assignExpr(p: *Parser) Error!Result { .div_assign_expr, .mod_assign_expr, => { - if (rhs.isZero()) { + if (rhs.val.isZero()) { switch (tag) { .div_assign_expr => try p.errStr(.division_by_zero, div.?, "division"), .mod_assign_expr => try p.errStr(.division_by_zero, mod.?, "remainder"), @@ -4492,7 +4325,7 @@ fn assignExpr(p: *Parser) Error!Result { try p.errStr(.incompatible_assign, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); } } else if (unqual_ty.isPtr()) { - if (rhs.isZero()) { + if (rhs.val.isZero()) { try rhs.nullCast(p, lhs.ty); } else if (rhs.ty.isInt()) { try p.errStr(.implicit_int_to_ptr, tok, try p.typePairStrExtra(rhs.ty, " to ", lhs.ty)); @@ -4547,22 +4380,22 @@ fn condExpr(p: *Parser) Error!Result { // Depending on the value of the condition, avoid evaluating unreachable branches. var then_expr = blk: { defer p.no_eval = saved_eval; - if (cond.val != .unavailable and !cond.getBool()) p.no_eval = true; + if (cond.val.tag != .unavailable and !cond.val.getBool()) p.no_eval = true; break :blk try p.expr(); }; try then_expr.expect(p); // TODO binary cond expr const colon = try p.expectToken(.colon); var else_expr = blk: { defer p.no_eval = saved_eval; - if (cond.val != .unavailable and cond.getBool()) p.no_eval = true; + if (cond.val.tag != .unavailable and cond.val.getBool()) p.no_eval = true; break :blk try p.condExpr(); }; try else_expr.expect(p); _ = try then_expr.adjustTypes(colon, &else_expr, p, .conditional); - if (cond.val != .unavailable) { - cond.val = if (cond.getBool()) then_expr.val else else_expr.val; + if (cond.val.tag != .unavailable) { + cond.val = if (cond.val.getBool()) then_expr.val else else_expr.val; } else { try then_expr.saveValue(p); try else_expr.saveValue(p); @@ -4584,7 +4417,7 @@ fn lorExpr(p: *Parser) Error!Result { defer p.no_eval = saved_eval; while (p.eatToken(.pipe_pipe)) |tok| { - if (lhs.val != .unavailable and lhs.getBool()) p.no_eval = true; + if (lhs.val.tag != .unavailable and lhs.val.getBool()) p.no_eval = true; var rhs = try p.landExpr(); try rhs.expect(p); @@ -4606,7 +4439,7 @@ fn landExpr(p: *Parser) Error!Result { defer p.no_eval = saved_eval; while (p.eatToken(.ampersand_ampersand)) |tok| { - if (lhs.val != .unavailable and !lhs.getBool()) p.no_eval = true; + if (lhs.val.tag != .unavailable and !lhs.val.getBool()) p.no_eval = true; var rhs = try p.orExpr(); try rhs.expect(p); @@ -4779,9 +4612,9 @@ fn addExpr(p: *Parser) Error!Result { if (try lhs.adjustTypes(minus.?, &rhs, p, if (plus != null) .add else .sub)) { if (plus != null) { - try lhs.add(plus.?, rhs, p); + if (lhs.val.add(lhs.val, rhs.val, lhs.ty, p.pp.comp)) try p.errOverflow(plus.?, lhs); } else { - try lhs.sub(minus.?, rhs, p); + if (lhs.val.sub(lhs.val, rhs.val, lhs.ty, p.pp.comp)) try p.errOverflow(minus.?, lhs); } } try lhs.bin(p, tag, rhs); @@ -4789,14 +4622,6 @@ fn addExpr(p: *Parser) Error!Result { return lhs; } -/// Implements C's % operator for signed integers, for evaluating constant expressions -/// caller guarantees rhs != 0 -/// caller guarantees lhs != std.math.minInt(i64) OR rhs != -1 -fn signedRemainder(lhs: i64, rhs: i64) i64 { - if (rhs > 0) return @rem(lhs, rhs); - return lhs - @divTrunc(lhs, rhs) * rhs; -} - /// mulExpr : castExpr (('*' | '/' | '%') castExpr)*ยด fn mulExpr(p: *Parser) Error!Result { var lhs = try p.castExpr(); @@ -4809,9 +4634,9 @@ fn mulExpr(p: *Parser) Error!Result { var rhs = try p.castExpr(); try rhs.expect(p); - if (rhs.isZero() and mul == null and !p.no_eval) { + if (rhs.val.isZero() and mul == null and !p.no_eval) { const err_tag: Diagnostics.Tag = if (p.in_macro) .division_by_zero_macro else .division_by_zero; - lhs.val = .unavailable; + lhs.val.tag = .unavailable; if (div != null) { try p.errStr(err_tag, div.?, "division"); } else { @@ -4822,23 +4647,21 @@ fn mulExpr(p: *Parser) Error!Result { if (try lhs.adjustTypes(percent.?, &rhs, p, if (tag == .mod_expr) .integer else .arithmetic)) { if (mul != null) { - try lhs.mul(mul.?, rhs, p); + if (lhs.val.mul(lhs.val, rhs.val, lhs.ty, p.pp.comp)) try p.errOverflow(mul.?, lhs); } else if (div != null) { - lhs.val = switch (lhs.val) { - .unsigned => |v| .{ .unsigned = v / rhs.val.unsigned }, - .signed => |v| .{ .signed = @divFloor(v, rhs.val.signed) }, - else => unreachable, - }; + lhs.val = Value.div(lhs.val, rhs.val, lhs.ty, p.pp.comp); } else { - if (lhs.val == .signed and lhs.val.signed == std.math.minInt(i64) and rhs.val.signed == -1) { - lhs.val = if (p.in_macro) .{ .signed = 0 } else .unavailable; - } else { - lhs.val = switch (lhs.val) { - .unsigned => |v| .{ .unsigned = v % rhs.val.unsigned }, - .signed => |v| .{ .signed = signedRemainder(v, rhs.val.signed) }, - else => unreachable, - }; + var res = Value.rem(lhs.val, rhs.val, lhs.ty, p.pp.comp); + if (res.tag == .unavailable) { + if (p.in_macro) { + // match clang behavior by defining invalid remainder to be zero in macros + res = Value.zero; + } else { + try lhs.saveValue(p); + try rhs.saveValue(p); + } } + lhs.val = res; } } @@ -4908,6 +4731,7 @@ fn castExpr(p: *Parser) Error!Result { try operand.expect(p); if (ty.is(.void)) { // everything can cast to void + operand.val.tag = .unavailable; } else if (ty.isInt() or ty.isFloat() or ty.isPtr()) { if (ty.isFloat() and operand.ty.isPtr()) try p.errStr(.invalid_cast_to_float, l_paren, try p.typeStr(operand.ty)); @@ -4925,6 +4749,7 @@ fn castExpr(p: *Parser) Error!Result { operand.val = .{ .signed = @bitCast(i64, copy) }; } } else { + operand.val.tag = .unavailable; try p.errStr(.invalid_cast_type, l_paren, try p.typeStr(operand.ty)); } if (ty.anyQual()) try p.errStr(.qual_cast, l_paren, try p.typeStr(ty)); @@ -4949,24 +4774,24 @@ fn builtinChooseExpr(p: *Parser) Error!Result { const l_paren = try p.expectToken(.l_paren); const cond_tok = p.tok_i; var cond = try p.constExpr(); - if (cond.val == .unavailable) { + if (cond.val.tag == .unavailable) { try p.errTok(.builtin_choose_cond, cond_tok); return error.ParsingFailed; } _ = try p.expectToken(.comma); - var then_expr = if (cond.getBool()) try p.assignExpr() else try p.parseNoEval(assignExpr); + var then_expr = if (cond.val.getBool()) try p.assignExpr() else try p.parseNoEval(assignExpr); try then_expr.expect(p); _ = try p.expectToken(.comma); - var else_expr = if (!cond.getBool()) try p.assignExpr() else try p.parseNoEval(assignExpr); + var else_expr = if (!cond.val.getBool()) try p.assignExpr() else try p.parseNoEval(assignExpr); try else_expr.expect(p); try p.expectClosing(l_paren, .r_paren); - if (cond.getBool()) { + if (cond.val.getBool()) { cond.val = then_expr.val; cond.ty = then_expr.ty; } else { @@ -5202,8 +5027,8 @@ fn unExpr(p: *Parser) Error!Result { try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); if (operand.ty.isInt()) try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); - if (operand.val != .unavailable) { - const res = @boolToInt(!operand.getBool()); + if (operand.val.tag != .unavailable) { + const res = @boolToInt(!operand.val.getBool()); operand.val = .{ .signed = res }; } operand.ty = .{ .specifier = .int }; @@ -5230,9 +5055,9 @@ fn unExpr(p: *Parser) Error!Result { } if (res.ty.sizeof(p.pp.comp)) |size| { - res.val = .{ .unsigned = size }; + res.val = .{ .tag = .int, .data = .{ .int = size } }; } else { - res.val = .unavailable; + res.val.tag = .unavailable; try p.errStr(.invalid_sizeof, expected_paren - 1, try p.typeStr(res.ty)); } res.ty = p.pp.comp.types.size; @@ -5538,7 +5363,7 @@ fn callExpr(p: *Parser, lhs: Result) Error!Result { try p.errTok(.parameter_here, params[arg_count].name_tok); } } else if (p_ty.isPtr()) { - if (arg.isZero()) { + if (arg.val.isZero()) { try arg.nullCast(p, p_ty); } else if (arg.ty.isInt()) { try p.errStr( @@ -5622,16 +5447,17 @@ fn callExpr(p: *Parser, lhs: Result) Error!Result { } fn checkArrayBounds(p: *Parser, index: Result, arr_ty: Type, tok: TokenIndex) !void { - const len = arr_ty.arrayLen() orelse return; - - switch (index.val) { - .unsigned => |val| if (std.math.compare(val, .gte, len)) - try p.errExtra(.array_after, tok, .{ .unsigned = val }), - .signed => |val| if (val < 0) - try p.errExtra(.array_before, tok, .{ .signed = val }) - else if (std.math.compare(val, .gte, len)) - try p.errExtra(.array_after, tok, .{ .unsigned = @intCast(u64, val) }), - .unavailable => return, + if (index.val.tag == .unavailable) return; + const len = Value.int(arr_ty.arrayLen() orelse return); + + if (index.ty.isUnsignedInt(p.pp.comp)) { + if (index.val.compare(.gte, len)) + try p.errExtra(.array_after, tok, .{ .unsigned = index.val.data.int }); + } else { + if (index.val.compare(.lt, Value.one)) + try p.errExtra(.array_before, tok, .{ .signed = index.val.getInt(i128) }) + else if (index.val.compare(.gte, len)) + try p.errExtra(.array_after, tok, .{ .unsigned = index.val.data.int }); } } diff --git a/src/Tree.zig b/src/Tree.zig index 2a475603..ca1bd41e 100644 --- a/src/Tree.zig +++ b/src/Tree.zig @@ -4,6 +4,7 @@ const Tokenizer = @import("Tokenizer.zig"); const Compilation = @import("Compilation.zig"); const Source = @import("Source.zig"); const Attribute = @import("Attribute.zig"); +const Value = @import("Value.zig"); const Tree = @This(); @@ -73,7 +74,7 @@ pub const Token = struct { pub const TokenIndex = u32; pub const NodeIndex = enum(u32) { none, _ }; -pub const ValueMap = std.AutoHashMap(NodeIndex, u64); +pub const ValueMap = std.AutoHashMap(NodeIndex, Value); comp: *Compilation, arena: std.heap.ArenaAllocator, @@ -567,7 +568,7 @@ pub fn isLvalExtra(nodes: Node.List.Slice, extra: []const NodeIndex, value_map: const data = nodes.items(.data)[@enumToInt(node)]; if (value_map.get(data.if3.cond)) |val| { - const offset = @boolToInt(val == 0); + const offset = @boolToInt(val.isZero()); return isLvalExtra(nodes, extra, value_map, extra[data.if3.body + offset], is_const); } return false; @@ -633,10 +634,9 @@ fn dumpNode(tree: Tree, node: NodeIndex, level: u32, w: anytype) @TypeOf(w).Erro } if (tree.value_map.get(node)) |val| { util.setColor(LITERAL, w); - if (ty.isUnsignedInt(tree.comp)) - try w.print(" (value: {d})", .{val}) - else - try w.print(" (value: {d})", .{@bitCast(i64, val)}); + try w.writeAll(" (value: "); + try val.dump(ty, tree.comp, w); + try w.writeByte(')'); } try w.writeAll("\n"); util.setColor(.reset, w); diff --git a/src/Value.zig b/src/Value.zig new file mode 100644 index 00000000..7bf63db0 --- /dev/null +++ b/src/Value.zig @@ -0,0 +1,382 @@ +const std = @import("std"); +const assert = std.debug.assert; +const Compilation = @import("Compilation.zig"); +const Type = @import("Type.zig"); + +const Value = @This(); + +tag: Tag = .unavailable, +data: union { + none: void, + int: u128, + float: f128, + array: []Value, + bytes: []u8, +} = .{ .none = {} }, + +const Tag = enum { + unavailable, + /// int is used to store integer, boolean and pointer values + int, + float, + array, + bytes, +}; + +pub const zero = int(0); +pub const one = int(1); + +pub fn int(v: anytype) Value { + return .{ .tag = .int, .data = .{ .int = v }}; +} + +/// Converts the stored value from a float to an integer. +/// `.unavailable` value remains unchanged. +pub fn floatToInt(v: *Value, old_ty: Type, new_ty: Type) void { + assert(old_ty.isFloat()); + if (v.tag == .unavailable) return; + _ = new_ty; +} + +/// Converts the stored value from an integer to a float. +/// `.unavailable` value remains unchanged. +pub fn intToFloat(v: *Value, old_ty: Type) void { + assert(old_ty.isInt()); + if (v.tag == .unavailable) return; +} + +/// Truncates or extends bits based on type. +/// old_ty is only used for size. +pub fn intCast(v: *Value, old_ty: Type, new_ty: Type) void { + _ = v; + _ = old_ty; + _ = new_ty; +} + +/// Truncates data.int to one bit +pub fn intToBool(v: *Value) void { + if (v.tag == .unavailable) return; + assert(v.tag == .int); + v.data.int = @boolToInt(v.getBool()); +} + +pub fn isZero(v: Value) bool { + return switch (v.tag) { + .unavailable => unreachable, + .int => v.data.int == 0, + .float => v.data.float == 0, + .array => unreachable, + .bytes => unreachable, + }; +} + +pub fn getBool(v: Value) bool { + return switch (v.tag) { + .unavailable => unreachable, + .int => v.data.int != 0, + .float => v.data.float != 0, + .array => unreachable, + .bytes => unreachable, + }; +} + +pub fn getInt(v: Value, comptime T: type) T { + if (T == u128) return v.data.int; + return if (@typeInfo(T).Int.signedness == .unsigned) + @truncate(T, v.data.int) + else + @truncate(T, @bitCast(i128, v.data.int)); +} + +pub fn getFloat(v: Value, comptime T: type) T { + if (T == f128) return v.data.float; + return @floatCast(T, v.data.float); +} + +pub fn add(res: *Value, a: Value, b: Value, ty: Type, comp: *Compilation) bool { + if (ty.isFloat()) return addFloat(res, a, b, ty, comp); + const size = ty.sizeof(comp).?; + const S = struct { + inline fn doAdd(comptime T: type, out: *u128, aa: Value, bb: Value) bool { + const a_val = aa.getInt(T); + const b_val = bb.getInt(T); + var c: T = undefined; + const overflow = @addWithOverflow(T, a_val, b_val, &c); + const OutT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); + out.* = @bitCast(OutT, c); + return overflow; + } + }; + var overflow = false; + var out: u128 = undefined; + if (ty.isUnsignedInt(comp)) switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => overflow = S.doAdd(u32, &out, a, b), + 8 => overflow = S.doAdd(u64, &out, a, b), + 16 => overflow = S.doAdd(u128, &out, a, b), + else => unreachable, + } else switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => overflow = S.doAdd(i32, &out, a, b), + 8 => overflow = S.doAdd(i64, &out, a, b), + 16 => overflow = S.doAdd(i128, &out, a, b), + else => unreachable, + } + res.* = .{ .tag = .int, .data = .{ .int = out } }; + return overflow; +} + +fn addFloat(res: *Value, a: Value, b: Value, ty: Type, comp: *Compilation) bool { + const size = ty.sizeof(comp).?; + const S = struct { + inline fn doAdd(comptime T: type, aa: Value, bb: Value) f128 { + const a_val = aa.getFloat(T); + const b_val = bb.getFloat(T); + return a_val + b_val; + } + }; + res.* = .{ .tag = .float, .data = .{ + .float = switch (size) { + 4 => S.doAdd(f32, a, b), + 8 => S.doAdd(f64, a, b), + 16 => S.doAdd(f128, a, b), + else => unreachable, + }, + } }; + return false; +} + +pub fn sub(res: *Value, a: Value, b: Value, ty: Type, comp: *Compilation) bool { + if (ty.isFloat()) return subFloat(res, a, b, ty, comp); + const size = ty.sizeof(comp).?; + const S = struct { + inline fn doSub(comptime T: type, out: *u128, aa: Value, bb: Value) bool { + const a_val = aa.getInt(T); + const b_val = bb.getInt(T); + var c: T = undefined; + const overflow = @subWithOverflow(T, a_val, b_val, &c); + const OutT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); + out.* = @bitCast(OutT, c); + return overflow; + } + }; + var overflow = false; + var out: u128 = undefined; + if (ty.isUnsignedInt(comp)) switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => overflow = S.doSub(u32, &out, a, b), + 8 => overflow = S.doSub(u64, &out, a, b), + 16 => overflow = S.doSub(u128, &out, a, b), + else => unreachable, + } else switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => overflow = S.doSub(i32, &out, a, b), + 8 => overflow = S.doSub(i64, &out, a, b), + 16 => overflow = S.doSub(i128, &out, a, b), + else => unreachable, + } + res.* = .{ .tag = .int, .data = .{ .int = out } }; + return overflow; +} + +fn subFloat(res: *Value, a: Value, b: Value, ty: Type, comp: *Compilation) bool { + const size = ty.sizeof(comp).?; + const S = struct { + inline fn doSub(comptime T: type, aa: Value, bb: Value) f128 { + const a_val = aa.getFloat(T); + const b_val = bb.getFloat(T); + return a_val - b_val; + } + }; + res.* = .{ .tag = .float, .data = .{ + .float = switch (size) { + 4 => S.doSub(f32, a, b), + 8 => S.doSub(f64, a, b), + 16 => S.doSub(f128, a, b), + else => unreachable, + }, + } }; + return false; +} + +pub fn mul(res: *Value, a: Value, b: Value, ty: Type, comp: *Compilation) bool { + if (ty.isFloat()) return mulFloat(res, a, b, ty, comp); + const size = ty.sizeof(comp).?; + const S = struct { + inline fn doMul(comptime T: type, out: *u128, aa: Value, bb: Value) bool { + const a_val = aa.getInt(T); + const b_val = bb.getInt(T); + var c: T = undefined; + const overflow = @mulWithOverflow(T, a_val, b_val, &c); + const OutT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); + out.* = @bitCast(OutT, c); + return overflow; + } + }; + var overflow = false; + var out: u128 = undefined; + if (ty.isUnsignedInt(comp)) switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => overflow = S.doMul(u32, &out, a, b), + 8 => overflow = S.doMul(u64, &out, a, b), + 16 => overflow = S.doMul(u128, &out, a, b), + else => unreachable, + } else switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => overflow = S.doMul(i32, &out, a, b), + 8 => overflow = S.doMul(i64, &out, a, b), + 16 => overflow = S.doMul(i128, &out, a, b), + else => unreachable, + } + res.* = .{ .tag = .int, .data = .{ .int = out } }; + return overflow; +} + +fn mulFloat(res: *Value, a: Value, b: Value, ty: Type, comp: *Compilation) bool { + const size = ty.sizeof(comp).?; + const S = struct { + inline fn doMul(comptime T: type, aa: Value, bb: Value) f128 { + const a_val = aa.getFloat(T); + const b_val = bb.getFloat(T); + return a_val * b_val; + } + }; + res.* = .{ .tag = .float, .data = .{ + .float = switch (size) { + 4 => S.doMul(f32, a, b), + 8 => S.doMul(f64, a, b), + 16 => S.doMul(f128, a, b), + else => unreachable, + }, + } }; + return false; +} + +pub fn div(a: Value, b: Value, ty: Type, comp: *Compilation) Value { + if (ty.isFloat()) return divFloat(a, b, ty, comp); + const size = ty.sizeof(comp).?; + const S = struct { + inline fn doDiv(comptime T: type, aa: Value, bb: Value) u128 { + const a_val = aa.getInt(T); + const b_val = bb.getInt(T); + const OutT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); + return @bitCast(OutT, @divTrunc(a_val, b_val)); + } + }; + return .{ + .tag = .int, + .data = .{ + .int = if (ty.isUnsignedInt(comp)) switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => S.doDiv(u32, a, b), + 8 => S.doDiv(u64, a, b), + 16 => S.doDiv(u128, a, b), + else => unreachable, + } else switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => S.doDiv(i32, a, b), + 8 => S.doDiv(i64, a, b), + 16 => S.doDiv(i128, a, b), + else => unreachable, + }, + }, + }; +} + +fn divFloat(a: Value, b: Value, ty: Type, comp: *Compilation) Value { + const size = ty.sizeof(comp).?; + const S = struct { + inline fn doDiv(comptime T: type, aa: Value, bb: Value) f128 { + const a_val = aa.getFloat(T); + const b_val = bb.getFloat(T); + return a_val / b_val; + } + }; + return .{ .tag = .float, .data = .{ + .float = switch (size) { + 4 => S.doDiv(f32, a, b), + 8 => S.doDiv(f64, a, b), + 16 => S.doDiv(f128, a, b), + else => unreachable, + }, + } }; +} + +pub fn rem(a: Value, b: Value, ty: Type, comp: *Compilation) Value { + const size = ty.sizeof(comp).?; + const S = struct { + /// Implements C's % operator for signed integers, for evaluating constant expressions + /// caller guarantees rhs != 0 + /// caller guarantees lhs != std.math.minInt(i64) OR rhs != -1 + inline fn signedRemainder(lhs: anytype, rhs: @TypeOf(lhs)) @TypeOf(lhs) { + if (rhs > 0) return @rem(lhs, rhs); + return lhs - @divTrunc(lhs, rhs) * rhs; + } + + inline fn doRem(comptime T: type, res: *Value, aa: Value, bb: Value) void { + const a_val = aa.getInt(T); + const b_val = bb.getInt(T); + + const OutT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); + if (@typeInfo(T).Int.signedness == .signed) { + if (a_val == std.math.minInt(T) and b_val == -1) { + res.tag = .unavailable; + } else { + res.data.int = @bitCast(OutT, signedRemainder(a_val, b_val)); + } + } else { + res.data.int = @bitCast(OutT, a_val % b_val); + } + } + }; + var out = Value{ .tag = .int, .data = .{ .int = 0 } }; + if (ty.isUnsignedInt(comp)) switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => S.doRem(u32, &out, a, b), + 8 => S.doRem(u64, &out, a, b), + 16 => S.doRem(u128, &out, a, b), + else => unreachable, + } else switch (size) { + 1 => unreachable, // promoted to int + 2 => unreachable, // promoted to int + 4 => S.doRem(i32, &out, a, b), + 8 => S.doRem(i64, &out, a, b), + 16 => S.doRem(i128, &out, a, b), + else => unreachable, + } + return out; +} + +pub fn compare(a: Value, op: std.math.CompareOperator, b: Value) bool { + _ = a; + _ = op; + _ = b; + return false; +} + +pub fn hash(v: Value) u64 { + _ = v; + return 0; +} + +pub fn dump(v: Value, ty: Type, comp: *Compilation, w: anytype) !void { + switch (v.tag) { + .unavailable => try w.writeAll("unavailable"), + .int => if (ty.isUnsignedInt(comp)) + try w.print("{d}", .{v.data.int}) + else + try w.print("{d}", .{@bitCast(i128, v.data.int)}), + // std.fmt does @as instead of @floatCast + .float => try w.print("{d}", .{@floatCast(f64, v.data.float)}), + else => try w.print("({s})", .{@tagName(v.tag)}), + } +} diff --git a/src/codegen/x86_64.zig b/src/codegen/x86_64.zig index 84cc30f4..1efc8f26 100644 --- a/src/codegen/x86_64.zig +++ b/src/codegen/x86_64.zig @@ -121,7 +121,7 @@ fn setReg(func: *Fn, val: Value, reg: Register) !void { } fn genNode(func: *Fn, node: NodeIndex) Codegen.Error!Value { - if (func.c.tree.value_map.get(node)) |some| return Value{ .immediate = @bitCast(i64, some) }; + if (func.c.tree.value_map.get(node)) |some| return Value{ .immediate = @truncate(i64, @bitCast(i128, some.data.int)) }; const data = func.c.node_data[@enumToInt(node)]; switch (func.c.node_tag[@enumToInt(node)]) { @@ -166,14 +166,14 @@ fn genNode(func: *Fn, node: NodeIndex) Codegen.Error!Value { return Value{ .none = {} }; }, .int_literal => return Value{ .immediate = @bitCast(i64, data.int) }, - .string_literal_expr => { - const str_bytes = func.c.tree.strings[data.str.index..][0..data.str.len]; - const section = try func.c.obj.getSection(.strings); - const start = section.items.len; - try section.appendSlice(str_bytes); - const symbol_name = try func.c.obj.declareSymbol(.strings, null, .Internal, .variable, start, str_bytes.len); - return Value{ .symbol = symbol_name }; - }, + // .string_literal_expr => { + // const str_bytes = func.c.tree.strings[data.str.index..][0..data.str.len]; + // const section = try func.c.obj.getSection(.strings); + // const start = section.items.len; + // try section.appendSlice(str_bytes); + // const symbol_name = try func.c.obj.declareSymbol(.strings, null, .Internal, .variable, start, str_bytes.len); + // return Value{ .symbol = symbol_name }; + // }, else => return func.c.comp.diag.fatalNoSrc("TODO x86_64 genNode {}\n", .{func.c.node_tag[@enumToInt(node)]}), } }