Skip to content

Commit

Permalink
Value: reloc cleanup
Browse files Browse the repository at this point in the history
Add better error and overflow handling and add some error messages
  • Loading branch information
ehaas committed Oct 5, 2024
1 parent 0ca705f commit 32199b9
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 98 deletions.
6 changes: 6 additions & 0 deletions src/aro/Diagnostics/messages.def
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,12 @@ unknown_warning
.opt = W("unknown-warning-option")
.kind = .warning

array_overflow
.msg = "{s}"
.extra = .str
.opt = W("array-bounds")
.kind = .warning

overflow
.msg = "overflow in expression; result is '{s}'"
.extra = .str
Expand Down
49 changes: 35 additions & 14 deletions src/aro/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ const Value = @import("Value.zig");
const SymbolStack = @import("SymbolStack.zig");
const Symbol = SymbolStack.Symbol;
const record_layout = @import("record_layout.zig");
const StrInt = @import("backend").StringInterner;
const backend = @import("backend");
const StrInt = backend.StringInterner;
const GlobalVarOffset = backend.Interner.Key.GlobalVarOffset;
const StringId = StrInt.StringId;
const Builtins = @import("Builtins.zig");
const Builtin = Builtins.Builtin;
Expand Down Expand Up @@ -377,6 +379,24 @@ fn errOverflow(p: *Parser, op_tok: TokenIndex, res: Result) !void {
try p.errStr(.overflow, op_tok, try res.str(p));
}

fn errArrayOverflow(p: *Parser, op_tok: TokenIndex, res: Result) !void {
const strings_top = p.strings.items.len;
defer p.strings.items.len = strings_top;

const w = p.strings.writer();
const format =
\\The pointer incremented by {s} refers past the last possible element in {d}-bit address space containing {d}-bit ({d}-byte) elements (max possible {d} elements)
;
const increment = try res.str(p);
const ptr_bits = p.comp.types.intptr.bitSizeof(p.comp).?;
const element_size = res.ty.elemType().sizeof(p.comp) orelse 1;
const max_elems = p.comp.maxArrayBytes() / element_size;

try w.print(format, .{ increment, ptr_bits, element_size * 8, element_size, max_elems });
const duped = try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
return p.errStr(.array_overflow, op_tok, duped);
}

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 }),
Expand Down Expand Up @@ -6669,8 +6689,13 @@ fn addExpr(p: *Parser) Error!Result {
const lhs_ty = lhs.ty;
if (try lhs.adjustTypes(minus.?, &rhs, p, if (plus != null) .add else .sub)) {
if (plus != null) {
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);
if (try lhs.val.add(lhs.val, rhs.val, lhs.ty, p.comp)) {
if (lhs.ty.isPtr()) {
try p.errArrayOverflow(plus.?, lhs);
} else if (lhs.ty.signedness(p.comp) != .unsigned) {
try p.errOverflow(plus.?, lhs);
}
}
} else {
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 @@ -7029,12 +7054,7 @@ 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 {
fn computeOffsetExtra(p: *Parser, node: NodeIndex, offset_so_far: i64) !GlobalVarOffset {
const tys = p.nodes.items(.ty);
const tags = p.nodes.items(.tag);
const data = p.nodes.items(.data);
Expand All @@ -7051,7 +7071,7 @@ fn computeOffsetExtra(p: *Parser, node: NodeIndex, offset_so_far: i64) !Reloc {
.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 };
return .{ .name = var_name, .offset = offset_so_far };
},
.array_access_expr => {
const bin_data = data[@intFromEnum(node)].bin;
Expand All @@ -7073,7 +7093,8 @@ fn computeOffsetExtra(p: *Parser, node: NodeIndex, offset_so_far: i64) !Reloc {
}
}

fn computeOffset(p: *Parser, node: NodeIndex) !Reloc {
/// Compute the offset (in bytes) of an expression from a base pointer.
fn computeOffset(p: *Parser, node: NodeIndex) !GlobalVarOffset {
return p.computeOffsetExtra(node, 0);
}

Expand Down Expand Up @@ -7134,14 +7155,14 @@ fn unExpr(p: *Parser) Error!Result {
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) {
const reloc: GlobalVarOffset = 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 };
break :blk .{ .name = .empty, .offset = 0 };
},
else => |er| return er,
};
addr_val = try Value.reloc(reloc.global, reloc.offset, p.comp);
addr_val = try Value.reloc(reloc, p.comp);
}
if (operand.ty.qual.register) try p.errTok(.addr_of_register, tok);

Expand Down
173 changes: 89 additions & 84 deletions src/aro/Value.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ 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 reloc(r: Interner.Key.GlobalVarOffset, comp: *Compilation) !Value {
return intern(comp, .{ .global_var_offset = r });
}

pub fn ref(v: Value) Interner.Ref {
Expand Down Expand Up @@ -249,12 +249,14 @@ pub const IntCastChangeKind = enum {
/// `.none` value remains unchanged.
pub fn intCast(v: *Value, dest_ty: Type, comp: *Compilation) !IntCastChangeKind {
if (v.opt_ref == .none) return .none;
const key = comp.interner.get(v.ref());
if (key == .global_var_offset) return .none;

const dest_bits: usize = @intCast(dest_ty.bitSizeof(comp).?);
const dest_signed = dest_ty.signedness(comp) == .signed;

var space: BigIntSpace = undefined;
const big = v.toBigInt(&space, comp);
const big = keyToBigInt(key, &space);
const value_bits = big.bitCountTwosComp();

// if big is negative, then is signed.
Expand Down Expand Up @@ -385,11 +387,12 @@ fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 {
}
}

pub fn toBigInt(val: Value, space: *BigIntSpace, comp: *const Compilation) BigIntConst {
return switch (comp.interner.get(val.ref()).int) {
inline .u64, .i64 => |x| BigIntMutable.init(&space.limbs, x).toConst(),
.big_int => |b| b,
};
fn keyToBigInt(key: Interner.Key, space: *BigIntSpace) BigIntConst {
return key.int.toBigInt(space);
}

fn toBigInt(val: Value, space: *BigIntSpace, comp: *const Compilation) BigIntConst {
return keyToBigInt(comp.interner.get(val.ref()), space);
}

pub fn isZero(v: Value, comp: *const Compilation) bool {
Expand Down Expand Up @@ -477,9 +480,10 @@ pub fn toBool(v: Value, comp: *const Compilation) bool {

pub fn toInt(v: Value, comptime T: type, comp: *const Compilation) ?T {
if (v.opt_ref == .none) return null;
if (comp.interner.get(v.ref()) != .int) return null;
const key = comp.interner.get(v.ref());
if (key != .int) return null;
var space: BigIntSpace = undefined;
const big_int = v.toBigInt(&space, comp);
const big_int = keyToBigInt(key, &space);
return big_int.to(T) catch null;
}

Expand Down Expand Up @@ -510,21 +514,6 @@ 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 All @@ -547,47 +536,53 @@ pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
};
res.* = try intern(comp, .{ .float = f });
return false;
} else {
var lhs_space: BigIntSpace = undefined;
var rhs_space: BigIntSpace = undefined;
const lhs_bigint = lhs.toBigInt(&lhs_space, comp);
const rhs_bigint = rhs.toBigInt(&rhs_space, comp);
}
const lhs_key = comp.interner.get(lhs.ref());
const rhs_key = comp.interner.get(rhs.ref());
if (lhs_key == .global_var_offset or rhs_key == .global_var_offset) {
const rel, const index = if (lhs_key == .global_var_offset)
.{ lhs_key.global_var_offset, rhs }
else
.{ rhs_key.global_var_offset, lhs };

const elem_size = try int(ty.elemType().sizeof(comp) orelse 1, comp);
var total_offset: Value = undefined;
const mul_overflow = try total_offset.mul(elem_size, index, comp.types.ptrdiff, comp);
const old_offset = try int(rel.offset, comp);
const add_overflow = try total_offset.add(total_offset, old_offset, comp.types.ptrdiff, comp);
_ = try total_offset.intCast(comp.types.ptrdiff, comp);
res.* = try reloc(.{ .name = rel.name, .offset = total_offset.toInt(i64, comp).? }, comp);
return mul_overflow or add_overflow;
}

const limbs = try comp.gpa.alloc(
std.math.big.Limb,
std.math.big.int.calcTwosCompLimbCount(bits),
);
defer comp.gpa.free(limbs);
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
var lhs_space: BigIntSpace = undefined;
var rhs_space: BigIntSpace = undefined;
const lhs_bigint = keyToBigInt(lhs_key, &lhs_space);
const rhs_bigint = keyToBigInt(rhs_key, &rhs_space);

const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, ty.signedness(comp), bits);
res.* = try intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
return overflowed;
}
const limbs = try comp.gpa.alloc(
std.math.big.Limb,
std.math.big.int.calcTwosCompLimbCount(bits),
);
defer comp.gpa.free(limbs);
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };

const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, ty.signedness(comp), bits);
res.* = try intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
return overflowed;
}

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

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

/// rhs_ty is only used when subtracting two pointers, so we can scale the result by the size of the element type
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 @@ -610,39 +605,48 @@ pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, rhs_ty: Type, comp: *C
};
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;
}
}
const lhs_key = comp.interner.get(lhs.ref());
const rhs_key = comp.interner.get(rhs.ref());
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;
if (lhs_reloc.name != rhs_reloc.name) {
res.* = .{};
return false;
}
var lhs_space: BigIntSpace = undefined;
var rhs_space: BigIntSpace = undefined;
const lhs_bigint = lhs.toBigInt(&lhs_space, comp);
const rhs_bigint = rhs.toBigInt(&rhs_space, comp);
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;
} else if (lhs_key == .global_var_offset) {
const rel = lhs_key.global_var_offset;

const elem_size = try int(ty.elemType().sizeof(comp) orelse 1, comp);
var total_offset: Value = undefined;
const mul_overflow = try total_offset.mul(elem_size, rhs, comp.types.ptrdiff, comp);
const old_offset = try int(rel.offset, comp);
const add_overflow = try total_offset.sub(total_offset, old_offset, comp.types.ptrdiff, undefined, comp);
_ = try total_offset.intCast(comp.types.ptrdiff, comp);
res.* = try reloc(.{ .name = rel.name, .offset = total_offset.toInt(i64, comp).? }, comp);
return mul_overflow or add_overflow;
}

const limbs = try comp.gpa.alloc(
std.math.big.Limb,
std.math.big.int.calcTwosCompLimbCount(bits),
);
defer comp.gpa.free(limbs);
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
var lhs_space: BigIntSpace = undefined;
var rhs_space: BigIntSpace = undefined;
const lhs_bigint = keyToBigInt(lhs_key, &lhs_space);
const rhs_bigint = keyToBigInt(rhs_key, &rhs_space);

const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, ty.signedness(comp), bits);
res.* = try intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
return overflowed;
}
const limbs = try comp.gpa.alloc(
std.math.big.Limb,
std.math.big.int.calcTwosCompLimbCount(bits),
);
defer comp.gpa.free(limbs);
var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };

const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, ty.signedness(comp), bits);
res.* = try intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
return overflowed;
}

pub fn mul(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool {
Expand Down Expand Up @@ -957,6 +961,7 @@ pub fn complexConj(val: Value, ty: Type, comp: *Compilation) !Value {
return intern(comp, .{ .complex = cf });
}

/// Returns null for values that cannot be compared at compile time (e.g. `&x < &y`) for globals `x` and `y`.
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;
Expand Down
4 changes: 4 additions & 0 deletions test/cases/relocations.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ struct __attribute__((packed)) Packed {
struct Packed packed;
_Static_assert(&packed.x - &packed.y == -1);

char *p = (char*)(&x + 100);

_Static_assert((char*)(&x+100) - (char*)&x == 400,"");

#define EXPECTED_ERRORS "relocations.c:24:1: error: static assertion failed" \
"relocations.c:29:16: error: static_assert expression is not an integral constant expression" \
"relocations.c:30:16: error: static_assert expression is not an integral constant expression" \
Expand Down

0 comments on commit 32199b9

Please sign in to comment.