Skip to content

Commit

Permalink
Parser: add frontend support for global var relocations
Browse files Browse the repository at this point in the history
  • Loading branch information
ehaas committed Oct 5, 2024
1 parent 01170ea commit 0ca705f
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 31 deletions.
1 change: 1 addition & 0 deletions src/aro/Attribute.zig
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ fn diagnoseField(
.array_ty,
.vector_ty,
.record_ty,
.global_var_offset,
=> unreachable,
});
}
Expand Down
4 changes: 4 additions & 0 deletions src/aro/Diagnostics/messages.def
Original file line number Diff line number Diff line change
Expand Up @@ -2530,3 +2530,7 @@ auto_type_self_initialized
.msg = "variable '{s}' declared with deduced type '__auto_type' cannot appear in its own initializer"
.extra = .str
.kind = .@"error"

non_constant_initializer
.msg = "initializer element is not a compile-time constant"
.kind = .@"error"
99 changes: 78 additions & 21 deletions src/aro/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,9 @@ pub fn valueChangedStr(p: *Parser, res: *Result, old_value: Value, int_ty: Type)
try w.writeAll(" changes ");
if (res.val.isZero(p.comp)) try w.writeAll("non-zero ");
try w.writeAll("value from ");
try old_value.print(res.ty, p.comp, w);
try old_value.print(res.ty, p.comp, p.comp.string_interner.getSlowTypeMapper(), w);
try w.writeAll(" to ");
try res.val.print(int_ty, p.comp, w);
try res.val.print(int_ty, p.comp, p.comp.string_interner.getSlowTypeMapper(), w);

return try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
}
Expand Down Expand Up @@ -1284,16 +1284,11 @@ fn staticAssert(p: *Parser) Error!bool {
try p.errStr(.pre_c23_compat, static_assert, "'_Static_assert' with no message");
}

// Array will never be zero; a value of zero for a pointer is a null pointer constant
if ((res.ty.isArray() or res.ty.isPtr()) and !res.val.isZero(p.comp)) {
const err_start = p.comp.diagnostics.list.items.len;
try p.errTok(.const_decl_folded, res_token);
if (res.ty.isPtr() and err_start != p.comp.diagnostics.list.items.len) {
// Don't show the note if the .const_decl_folded diagnostic was not added
try p.errTok(.constant_expression_conversion_not_allowed, res_token);
}
}
const is_int_expr = res.ty.isInt();
try res.boolCast(p, .{ .specifier = .bool }, res_token);
if (!is_int_expr) {
res.val = .{};
}
if (res.val.opt_ref == .none) {
if (res.ty.specifier != .invalid) {
try p.errTok(.static_assert_not_constant, res_token);
Expand Down Expand Up @@ -5183,7 +5178,7 @@ pub const Result = struct {
const strings_top = p.strings.items.len;
defer p.strings.items.len = strings_top;

try res.val.print(res.ty, p.comp, p.strings.writer());
try res.val.print(res.ty, p.comp, p.comp.string_interner.getSlowTypeMapper(), p.strings.writer());
return try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
}

Expand Down Expand Up @@ -6589,8 +6584,8 @@ fn eqExpr(p: *Parser) Error!Result {

if (try lhs.adjustTypes(ne.?, &rhs, p, .equality)) {
const op: std.math.CompareOperator = if (tag == .equal_expr) .eq else .neq;
const res = lhs.val.compare(op, rhs.val, p.comp);
lhs.val = Value.fromBool(res);
const res = lhs.val.compareExtra(op, rhs.val, p.comp);
lhs.val = if (res) |val| Value.fromBool(val) else .{};
} else {
lhs.val.boolCast(p.comp);
}
Expand Down Expand Up @@ -6620,8 +6615,8 @@ fn compExpr(p: *Parser) Error!Result {
.greater_than_equal_expr => .gte,
else => unreachable,
};
const res = lhs.val.compare(op, rhs.val, p.comp);
lhs.val = Value.fromBool(res);
const res = lhs.val.compareExtra(op, rhs.val, p.comp);
lhs.val = if (res) |val| Value.fromBool(val) else .{};
} else {
lhs.val.boolCast(p.comp);
}
Expand Down Expand Up @@ -6677,7 +6672,7 @@ fn addExpr(p: *Parser) Error!Result {
if (try lhs.val.add(lhs.val, rhs.val, lhs.ty, p.comp) and
lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(plus.?, lhs);
} else {
if (try lhs.val.sub(lhs.val, rhs.val, lhs.ty, p.comp) and
if (try lhs.val.sub(lhs.val, rhs.val, lhs.ty, rhs.ty, p.comp) and
lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(minus.?, lhs);
}
}
Expand Down Expand Up @@ -7034,6 +7029,54 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re
return Result{ .ty = base_ty, .val = val, .node = lhs.node };
}

const Reloc = struct {
global: StringId,
offset: i64,
};

fn computeOffsetExtra(p: *Parser, node: NodeIndex, offset_so_far: i64) !Reloc {
const tys = p.nodes.items(.ty);
const tags = p.nodes.items(.tag);
const data = p.nodes.items(.data);
const tag = tags[@intFromEnum(node)];

switch (tag) {
.implicit_cast => {
const cast_data = data[@intFromEnum(node)].cast;
return switch (cast_data.kind) {
.array_to_pointer => p.computeOffsetExtra(cast_data.operand, offset_so_far),
else => unreachable,
};
},
.paren_expr => return p.computeOffsetExtra(data[@intFromEnum(node)].un, offset_so_far),
.decl_ref_expr => {
const var_name = try p.comp.internString(p.tokSlice(data[@intFromEnum(node)].decl_ref));
return .{ .global = var_name, .offset = offset_so_far };
},
.array_access_expr => {
const bin_data = data[@intFromEnum(node)].bin;
const ty = tys[@intFromEnum(node)];

const index_val = p.value_map.get(bin_data.rhs) orelse return error.InvalidReloc;
const as_int = index_val.toInt(i64, p.comp).?;
const size = ty.sizeof(p.comp).?;
const this_offset = @as(i64, @intCast(size)) * as_int;
return p.computeOffsetExtra(bin_data.lhs, this_offset + offset_so_far);
},
.member_access_expr => {
const member = data[@intFromEnum(node)].member;
const record = tys[@intFromEnum(member.lhs)].getRecord().?;
const field_offset: i64 = @intCast(@divExact(record.fields[member.index].layout.offset_bits, 8));
return p.computeOffsetExtra(member.lhs, offset_so_far + field_offset);
},
else => return error.InvalidReloc,
}
}

fn computeOffset(p: *Parser, node: NodeIndex) !Reloc {
return p.computeOffsetExtra(node, 0);
}

/// unExpr
/// : (compoundLiteral | primaryExpr) suffixExpr*
/// | '&&' IDENTIFIER
Expand Down Expand Up @@ -7074,22 +7117,35 @@ fn unExpr(p: *Parser) Error!Result {
try p.err(.invalid_preproc_operator);
return error.ParsingFailed;
}
const ampersand_tok = p.tok_i;
p.tok_i += 1;
var operand = try p.castExpr();
try operand.expect(p);
var addr_val: Value = .{};

const tree = p.tmpTree();
if (p.getNode(operand.node, .member_access_expr) orelse
p.getNode(operand.node, .member_access_ptr_expr)) |member_node|
{
if (tree.isBitfield(member_node)) try p.errTok(.addr_of_bitfield, tok);
}
if (!tree.isLval(operand.node) and !operand.ty.is(.invalid)) {
const operand_ty_valid = !operand.ty.is(.invalid);
if (!tree.isLval(operand.node) and operand_ty_valid) {
try p.errTok(.addr_of_rvalue, tok);
} else if (operand_ty_valid and p.func.ty == null) {
// address of global
const reloc: Reloc = p.computeOffset(operand.node) catch |e| switch (e) {
error.InvalidReloc => blk: {
try p.errTok(.non_constant_initializer, ampersand_tok);
break :blk .{ .global = .empty, .offset = 0 };
},
else => |er| return er,
};
addr_val = try Value.reloc(reloc.global, reloc.offset, p.comp);
}
if (operand.ty.qual.register) try p.errTok(.addr_of_register, tok);

if (!operand.ty.is(.invalid)) {
if (operand_ty_valid) {
const elem_ty = try p.arena.create(Type);
elem_ty.* = operand.ty;
operand.ty = Type{
Expand All @@ -7099,6 +7155,7 @@ fn unExpr(p: *Parser) Error!Result {
}
try operand.saveValue(p);
try operand.un(p, .addr_of_expr, tok);
operand.val = addr_val;
return operand;
},
.asterisk => {
Expand Down Expand Up @@ -7144,7 +7201,7 @@ fn unExpr(p: *Parser) Error!Result {

try operand.usualUnaryConversion(p, tok);
if (operand.val.isArithmetic(p.comp)) {
_ = try operand.val.sub(Value.zero, operand.val, operand.ty, p.comp);
_ = try operand.val.negate(operand.val, operand.ty, p.comp);
} else {
operand.val = .{};
}
Expand Down Expand Up @@ -7198,7 +7255,7 @@ fn unExpr(p: *Parser) Error!Result {
try operand.usualUnaryConversion(p, tok);

if (operand.val.is(.int, p.comp) or operand.val.is(.int, p.comp)) {
if (try operand.val.sub(operand.val, Value.one, operand.ty, p.comp))
if (try operand.val.decrement(operand.val, operand.ty, p.comp))
try p.errOverflow(tok, operand);
} else {
operand.val = .{};
Expand Down
2 changes: 1 addition & 1 deletion src/aro/Tree.zig
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ fn dumpNode(
if (tree.value_map.get(node)) |val| {
try config.setColor(w, LITERAL);
try w.writeAll(" (value: ");
try val.print(ty, tree.comp, w);
try val.print(ty, tree.comp, mapper, w);
try w.writeByte(')');
}
if (tag == .implicit_return and data.return_zero) {
Expand Down
93 changes: 89 additions & 4 deletions src/aro/Value.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const BigIntConst = std.math.big.int.Const;
const BigIntMutable = std.math.big.int.Mutable;
const backend = @import("backend");
const Interner = backend.Interner;
const StringInterner = backend.StringInterner;
const StringId = StringInterner.StringId;
const BigIntSpace = Interner.Tag.Int.BigIntSpace;
const Compilation = @import("Compilation.zig");
const Type = @import("Type.zig");
Expand Down Expand Up @@ -32,6 +34,10 @@ pub fn int(i: anytype, comp: *Compilation) !Value {
}
}

pub fn reloc(name: StringId, offset: i64, comp: *Compilation) !Value {
return intern(comp, .{ .global_var_offset = .{ .name = name, .offset = offset } });
}

pub fn ref(v: Value) Interner.Ref {
std.debug.assert(v.opt_ref != .none);
return @enumFromInt(@intFromEnum(v.opt_ref));
Expand Down Expand Up @@ -407,6 +413,7 @@ pub fn isZero(v: Value, comp: *const Compilation) bool {
inline else => |data| return data[0] == 0.0 and data[1] == 0.0,
},
.bytes => return false,
.global_var_offset => return false,
else => unreachable,
}
}
Expand Down Expand Up @@ -503,6 +510,21 @@ fn complexAddSub(lhs: Value, rhs: Value, comptime T: type, op: ComplexOp, comp:

pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool {
const bits: usize = @intCast(ty.bitSizeof(comp).?);
if (ty.isPtr()) blk: {
const lhs_key = comp.interner.get(lhs.ref());
const rhs_key = comp.interner.get(rhs.ref());
const rel, const delta = if (lhs_key == .global_var_offset)
.{ lhs_key.global_var_offset, rhs.toInt(i64, comp).? }
else if (rhs_key == .global_var_offset)
.{ rhs_key.global_var_offset, lhs.toInt(i64, comp).? }
else
break :blk;

const elem_size: i64 = @intCast(ty.elemType().sizeof(comp) orelse 1);
const total_offset = rel.offset + elem_size * delta;
res.* = try reloc(rel.name, total_offset, comp);
return false;
}
if (ty.isFloat()) {
if (ty.isComplex()) {
res.* = switch (bits) {
Expand Down Expand Up @@ -544,8 +566,28 @@ pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
}
}

pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool {
pub fn negate(res: *Value, val: Value, ty: Type, comp: *Compilation) !bool {
return res.sub(zero, val, ty, Type.int, comp);
}

pub fn decrement(res: *Value, val: Value, ty: Type, comp: *Compilation) !bool {
return res.sub(val, one, ty, Type.int, comp);
}

pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, rhs_ty: Type, comp: *Compilation) !bool {
const bits: usize = @intCast(ty.bitSizeof(comp).?);
if (ty.isPtr()) {
const maybe_rel = comp.interner.get(lhs.ref());
if (maybe_rel == .global_var_offset) {
const rel = maybe_rel.global_var_offset;
const delta = rhs.toInt(i64, comp).?;

const elem_size: i64 = @intCast(ty.elemType().sizeof(comp) orelse 1);
const total_offset = rel.offset - elem_size * delta;
res.* = try reloc(rel.name, total_offset, comp);
return false;
}
}
if (ty.isFloat()) {
if (ty.isComplex()) {
res.* = switch (bits) {
Expand All @@ -569,6 +611,22 @@ pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
res.* = try intern(comp, .{ .float = f });
return false;
} else {
if (rhs_ty.isPtr()) {
const maybe_lhs_reloc = comp.interner.get(lhs.ref());
const maybe_rhs_reloc = comp.interner.get(rhs.ref());
if (maybe_lhs_reloc == .global_var_offset and maybe_rhs_reloc == .global_var_offset) {
const lhs_reloc = maybe_lhs_reloc.global_var_offset;
const rhs_reloc = maybe_rhs_reloc.global_var_offset;
if (lhs_reloc.name != rhs_reloc.name) {
res.* = .{};
return false;
}
const difference, const overflowed = @subWithOverflow(lhs_reloc.offset, rhs_reloc.offset);
const rhs_size: i64 = @intCast(rhs_ty.elemType().sizeof(comp) orelse 1);
res.* = try int(@divTrunc(difference, rhs_size), comp);
return overflowed != 0;
}
}
var lhs_space: BigIntSpace = undefined;
var rhs_space: BigIntSpace = undefined;
const lhs_bigint = lhs.toBigInt(&lhs_space, comp);
Expand Down Expand Up @@ -723,7 +781,7 @@ pub fn rem(lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !Value {
var tmp: Value = undefined;
_ = try tmp.div(lhs, rhs, ty, comp);
_ = try tmp.mul(tmp, rhs, ty, comp);
_ = try tmp.sub(lhs, tmp, ty, comp);
_ = try tmp.sub(lhs, tmp, ty, Type.int, comp);
return tmp;
}
}
Expand Down Expand Up @@ -899,7 +957,7 @@ pub fn complexConj(val: Value, ty: Type, comp: *Compilation) !Value {
return intern(comp, .{ .complex = cf });
}

pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *const Compilation) bool {
pub fn compareExtra(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *const Compilation) ?bool {
if (op == .eq) {
return lhs.opt_ref == rhs.opt_ref;
} else if (lhs.opt_ref == rhs.opt_ref) {
Expand All @@ -919,6 +977,19 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons
const imag_equal = std.math.compare(lhs.imag(f128, comp), .eq, rhs.imag(f128, comp));
return !real_equal or !imag_equal;
}
if (lhs_key == .global_var_offset and rhs_key == .global_var_offset) {
const lhs_reloc = lhs_key.global_var_offset;
const rhs_reloc = rhs_key.global_var_offset;
switch (op) {
.eq => if (lhs_reloc.name != rhs_reloc.name) return false,
.neq => if (lhs_reloc.name != rhs_reloc.name) return true,
else => if (lhs_reloc.name != rhs_reloc.name) return null,
}

return std.math.compare(lhs_reloc.offset, op, rhs_reloc.offset);
} else if (lhs_key == .global_var_offset or rhs_key == .global_var_offset) {
return null;
}

var lhs_bigint_space: BigIntSpace = undefined;
var rhs_bigint_space: BigIntSpace = undefined;
Expand All @@ -927,6 +998,10 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons
return lhs_bigint.order(rhs_bigint).compare(op);
}

pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *const Compilation) bool {
return lhs.compareExtra(op, rhs, comp).?;
}

fn twosCompIntLimit(limit: std.math.big.int.TwosCompIntLimit, ty: Type, comp: *Compilation) !Value {
const signedness = ty.signedness(comp);
if (limit == .min and signedness == .unsigned) return Value.zero;
Expand Down Expand Up @@ -963,7 +1038,7 @@ pub fn maxInt(ty: Type, comp: *Compilation) !Value {
return twosCompIntLimit(.max, ty, comp);
}

pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w).Error!void {
pub fn print(v: Value, ty: Type, comp: *const Compilation, mapper: StringInterner.TypeMapper, w: anytype) @TypeOf(w).Error!void {
if (ty.is(.bool)) {
return w.writeAll(if (v.isZero(comp)) "false" else "true");
}
Expand All @@ -983,6 +1058,16 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w
.cf32 => |components| return w.print("{d} + {d}i", .{ @round(@as(f64, @floatCast(components[0])) * 1000000) / 1000000, @round(@as(f64, @floatCast(components[1])) * 1000000) / 1000000 }),
inline else => |components| return w.print("{d} + {d}i", .{ @as(f64, @floatCast(components[0])), @as(f64, @floatCast(components[1])) }),
},
.global_var_offset => |rel| {
const name = mapper.lookup(rel.name);
try w.print("&{s}", .{name});
if (rel.offset == 0) return;
if (rel.offset == std.math.minInt(i64)) {
return w.print(" - {d}", .{std.math.maxInt(i64) + 1});
}
const sign: u8 = if (rel.offset < 0) '-' else '+';
return w.print(" {c} {d}", .{ sign, @abs(rel.offset) });
},
else => unreachable, // not a value
}
}
Expand Down
Loading

0 comments on commit 0ca705f

Please sign in to comment.