Commit e13a182990

g-w1 <58830309+g-w1@users.noreply.github.com>
2021-06-21 17:47:34
stage2 Sema: implement @intToPtr (#9144)
Co-authored-by: Veikka Tuominen <git@vexu.eu>
1 parent a95fdb0
Changed files (5)
src/codegen.zig
@@ -4169,6 +4169,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
                         }
                     }
+                    if (typed_value.val.tag() == .int_u64) {
+                        return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
+                    }
                     return self.fail(src, "TODO codegen more kinds of const pointers", .{});
                 },
                 .Int => {
src/Sema.zig
@@ -5758,7 +5758,65 @@ fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
 fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const src = inst_data.src();
-    return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToPtr", .{});
+
+    const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+
+    const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+    const operand_res = try sema.resolveInst(extra.rhs);
+    const operand_coerced = try sema.coerce(block, Type.initTag(.usize), operand_res, operand_src);
+
+    const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const type_res = try sema.resolveType(block, src, extra.lhs);
+    if (type_res.zigTypeTag() != .Pointer)
+        return sema.mod.fail(&block.base, type_src, "expected pointer, found '{}'", .{type_res});
+    const ptr_align = type_res.ptrAlignment(sema.mod.getTarget());
+
+    const uncasted_operand = try sema.resolveInst(extra.rhs);
+    if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
+        const addr = val.toUnsignedInt();
+        if (!type_res.isAllowzeroPtr() and addr == 0)
+            return sema.mod.fail(&block.base, operand_src, "pointer type '{}' does not allow address zero", .{type_res});
+        if (addr != 0 and addr % ptr_align != 0)
+            return sema.mod.fail(&block.base, operand_src, "pointer type '{}' requires aligned address", .{type_res});
+
+        const val_payload = try sema.arena.create(Value.Payload.U64);
+        val_payload.* = .{
+            .base = .{ .tag = .int_u64 },
+            .data = addr,
+        };
+        return sema.mod.constInst(sema.arena, src, .{
+            .ty = type_res,
+            .val = Value.initPayload(&val_payload.base),
+        });
+    }
+
+    try sema.requireRuntimeBlock(block, src);
+    if (block.wantSafety()) {
+        const zero = try sema.mod.constInst(sema.arena, src, .{
+            .ty = Type.initTag(.u64),
+            .val = Value.initTag(.zero),
+        });
+        if (!type_res.isAllowzeroPtr()) {
+            const is_non_zero = try block.addBinOp(src, Type.initTag(.bool), .cmp_neq, operand_coerced, zero);
+            try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);
+        }
+
+        if (ptr_align > 1) {
+            const val_payload = try sema.arena.create(Value.Payload.U64);
+            val_payload.* = .{
+                .base = .{ .tag = .int_u64 },
+                .data = ptr_align - 1,
+            };
+            const align_minus_1 = try sema.mod.constInst(sema.arena, src, .{
+                .ty = Type.initTag(.u64),
+                .val = Value.initPayload(&val_payload.base),
+            });
+            const remainder = try block.addBinOp(src, Type.initTag(.u64), .bit_and, operand_coerced, align_minus_1);
+            const is_aligned = try block.addBinOp(src, Type.initTag(.bool), .cmp_eq, remainder, zero);
+            try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment);
+        }
+    }
+    return block.addUnOp(src, type_res, .bitcast, operand_coerced);
 }
 
 fn zirErrSetCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
@@ -6183,6 +6241,8 @@ pub const PanicId = enum {
     unreach,
     unwrap_null,
     unwrap_errunion,
+    cast_to_null,
+    incorrect_alignment,
     invalid_error_code,
 };
 
@@ -6805,10 +6865,13 @@ fn storePtr(
     if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
         return;
 
-    if (try sema.resolvePossiblyUndefinedValue(block, src, ptr)) |ptr_val| {
+    if (try sema.resolvePossiblyUndefinedValue(block, src, ptr)) |ptr_val| blk: {
         const const_val = (try sema.resolvePossiblyUndefinedValue(block, src, value)) orelse
             return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{});
 
+        if (ptr_val.tag() == .int_u64)
+            break :blk; // propogate it down to runtime
+
         const comptime_alloc = ptr_val.castTag(.comptime_alloc).?;
         if (comptime_alloc.data.runtime_index < block.runtime_index) {
             if (block.runtime_cond) |cond_src| {
@@ -6947,7 +7010,10 @@ fn analyzeLoad(
         .Pointer => ptr.ty.elemType(),
         else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}),
     };
-    if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
+    if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| blk: {
+        if (ptr_val.tag() == .int_u64)
+            break :blk; // do it at runtime
+
         return sema.mod.constInst(sema.arena, src, .{
             .ty = elem_ty,
             .val = try ptr_val.pointerDeref(sema.arena),
src/type.zig
@@ -1538,7 +1538,7 @@ pub const Type = extern union {
             .optional_single_const_pointer,
             .optional_single_mut_pointer,
             => {
-                if (self.elemType().hasCodeGenBits()) return 1;
+                if (!self.elemType().hasCodeGenBits()) return 1;
                 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
             },
 
@@ -1550,7 +1550,7 @@ pub const Type = extern union {
             .c_mut_pointer,
             .pointer,
             => {
-                if (self.elemType().hasCodeGenBits()) return 0;
+                if (!self.elemType().hasCodeGenBits()) return 0;
                 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
             },
 
src/value.zig
@@ -979,6 +979,26 @@ pub const Value = extern union {
         };
     }
 
+    /// Asserts the value is numeric
+    pub fn isZero(self: Value) bool {
+        return switch (self.tag()) {
+            .zero => true,
+            .one => false,
+
+            .int_u64 => self.castTag(.int_u64).?.data == 0,
+            .int_i64 => self.castTag(.int_i64).?.data == 0,
+
+            .float_16 => self.castTag(.float_16).?.data == 0,
+            .float_32 => self.castTag(.float_32).?.data == 0,
+            .float_64 => self.castTag(.float_64).?.data == 0,
+            .float_128 => self.castTag(.float_128).?.data == 0,
+
+            .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().eqZero(),
+            .int_big_negative => self.castTag(.int_big_negative).?.asBigInt().eqZero(),
+            else => unreachable,
+        };
+    }
+
     pub fn orderAgainstZero(lhs: Value) std.math.Order {
         return switch (lhs.tag()) {
             .zero,
test/stage2/test.zig
@@ -1000,6 +1000,24 @@ pub fn addCases(ctx: *TestContext) !void {
         \\}
     , &[_][]const u8{":2:3: error: this is an error"});
 
+    {
+        var case = ctx.exe("intToPtr", linux_x64);
+        case.addError(
+            \\pub fn main() void {
+            \\    _ = @intToPtr(*u8, 0);
+            \\}
+        , &[_][]const u8{
+            ":2:24: error: pointer type '*u8' does not allow address zero",
+        });
+        case.addError(
+            \\pub fn main() void {
+            \\    _ = @intToPtr(*u32, 2);
+            \\}
+        , &[_][]const u8{
+            ":2:25: error: pointer type '*u32' requires aligned address",
+        });
+    }
+
     {
         var case = ctx.obj("variable shadowing", linux_x64);
         case.addError(