Commit 90bce11f62

Veikka Tuominen <git@vexu.eu>
2022-02-28 12:34:21
stage2: implement `@frameAddress`
1 parent 2682b41
src/arch/aarch64/CodeGen.zig
@@ -670,6 +670,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
             .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
+
+            .frame_address => try self.airFrameAddress(inst),
             // zig fmt: on
         }
 
@@ -1517,6 +1519,13 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
+fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+    const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
+    _ = extra;
+    return self.fail("TODO implement codegen airFrameAddress", .{});
+}
+
 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch});
src/arch/arm/CodeGen.zig
@@ -656,6 +656,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
             .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
+
+            .frame_address => try self.airFrameAddress(inst),
             // zig fmt: on
         }
 
@@ -1272,6 +1274,13 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
+fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+    const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress", .{});
+    return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
src/arch/riscv64/CodeGen.zig
@@ -641,6 +641,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
             .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
+
+            .frame_address => try self.airFrameAddress(inst),
             // zig fmt: on
         }
         if (std.debug.runtime_safety) {
@@ -1103,6 +1105,12 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
+fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
+    _ = self;
+    _ = inst;
+    return self.fail("TODO implement codegen airFrameAddress", .{});
+}
+
 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch});
src/arch/wasm/CodeGen.zig
@@ -1726,6 +1726,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
         .error_name,
         .errunion_payload_ptr_set,
         .field_parent_ptr,
+        .frame_address,
 
         // For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248
         // is implemented in the frontend before implementing them here in the wasm backend.
src/arch/x86_64/CodeGen.zig
@@ -753,6 +753,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .wrap_optional         => try self.airWrapOptional(inst),
             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
             .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
+
+            .frame_address => try self.airFrameAddress(inst),
             // zig fmt: on
         }
 
@@ -1865,6 +1867,15 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
+fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const result: MCValue = if (self.liveness.isUnused(inst))
+        .dead
+    else
+        return self.fail("TODO implement airFrameAddress for {}", .{self.target.cpu.arch});
+    return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
src/codegen/c.zig
@@ -1757,6 +1757,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .wrap_errunion_payload       => try airWrapErrUnionPay(f, inst),
             .wrap_errunion_err           => try airWrapErrUnionErr(f, inst),
             .errunion_payload_ptr_set    => try airErrUnionPayloadPtrSet(f, inst),
+
+            .frame_address => try airFrameAddress(f, inst),
             // zig fmt: on
         };
         switch (result_value) {
@@ -3198,6 +3200,11 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
     return f.fail("TODO: C backend: implement airErrUnionPayloadPtrSet", .{});
 }
 
+fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
+    _ = inst;
+    return f.fail("TODO: C backend: implement airFrameAddress", .{});
+}
+
 fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
     if (f.liveness.isUnused(inst))
         return CValue.none;
src/codegen/llvm.zig
@@ -2214,6 +2214,8 @@ pub const FuncGen = struct {
                 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
                 .wrap_errunion_err     => try self.airWrapErrUnionErr(inst),
 
+                .frame_address => try self.airFrameAddress(inst),
+
                 .constant => unreachable,
                 .const_ty => unreachable,
                 .unreach  => self.airUnreach(inst),
@@ -3339,6 +3341,16 @@ pub const FuncGen = struct {
         return partial;
     }
 
+    fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+        if (self.liveness.isUnused(inst)) return null;
+
+        const llvm_i32 = try self.dg.llvmType(Type.initTag(.i32));
+        const llvm_fn = self.getIntrinsic("llvm.frameaddress", &.{llvm_i32});
+        const ptr_val = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{llvm_i32.constNull()}, 1, .Fast, .Auto, "");
+        const llvm_usize = try self.dg.llvmType(Type.usize);
+        return self.builder.buildPtrToInt(ptr_val, llvm_usize, "");
+    }
+
     fn airMin(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
         if (self.liveness.isUnused(inst)) return null;
 
src/Air.zig
@@ -580,6 +580,10 @@ pub const Inst = struct {
         /// Uses the `ty_pl` field.
         field_parent_ptr,
 
+        /// Implements @frameAddress builtin.
+        /// Uses the `ty` field.
+        frame_address,
+
         pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
             return switch (op) {
                 .lt => .cmp_lt,
@@ -939,6 +943,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
         .ptrtoint,
         .slice_len,
         .ret_addr,
+        .frame_address,
         => return Type.initTag(.usize),
 
         .bool_to_int => return Type.initTag(.u1),
src/Liveness.zig
@@ -317,6 +317,7 @@ fn analyzeInst(
         .unreach,
         .fence,
         .ret_addr,
+        .frame_address,
         => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
 
         .not,
src/print_air.zig
@@ -177,6 +177,7 @@ const Writer = struct {
             .alloc,
             .ret_ptr,
             .arg,
+            .frame_address,
             => try w.writeTy(s, inst),
 
             .not,
src/Sema.zig
@@ -8078,9 +8078,10 @@ fn analyzeTupleCat(
     if (dest_fields == 0) {
         return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
     }
+    const final_len = try sema.usizeCast(block, rhs_src, dest_fields);
 
-    const types = try sema.arena.alloc(Type, dest_fields);
-    const values = try sema.arena.alloc(Value, dest_fields);
+    const types = try sema.arena.alloc(Type, final_len);
+    const values = try sema.arena.alloc(Value, final_len);
 
     const opt_runtime_src = rs: {
         var runtime_src: ?LazySrcLoc = null;
@@ -8116,7 +8117,7 @@ fn analyzeTupleCat(
 
     try sema.requireRuntimeBlock(block, runtime_src);
 
-    const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_fields);
+    const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
     for (lhs_tuple.types) |_, i| {
         const operand_src = lhs_src; // TODO better source location
         element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, @intCast(u32, i), lhs_ty);
@@ -8261,9 +8262,10 @@ fn analyzeTupleMul(
     if (final_len_u64 == 0) {
         return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
     }
+    const final_len = try sema.usizeCast(block, rhs_src, final_len_u64);
 
-    const types = try sema.arena.alloc(Type, final_len_u64);
-    const values = try sema.arena.alloc(Value, final_len_u64);
+    const types = try sema.arena.alloc(Type, final_len);
+    const values = try sema.arena.alloc(Value, final_len);
 
     const opt_runtime_src = rs: {
         var runtime_src: ?LazySrcLoc = null;
@@ -8295,7 +8297,7 @@ fn analyzeTupleMul(
 
     try sema.requireRuntimeBlock(block, runtime_src);
 
-    const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len_u64);
+    const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
     for (operand_tuple.types) |_, i| {
         const operand_src = lhs_src; // TODO better source location
         element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(u32, i), operand_ty);
@@ -11893,7 +11895,8 @@ fn zirFrameAddress(
     extended: Zir.Inst.Extended.InstData,
 ) CompileError!Air.Inst.Ref {
     const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
-    return sema.fail(block, src, "TODO: Sema.zirFrameAddress", .{});
+    try sema.requireFunctionBlock(block, src);
+    return block.addTy(.frame_address, Type.@"usize");
 }
 
 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -19257,6 +19260,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr
 /// Used to convert a u64 value to a usize value, emitting a compile error if the number
 /// is too big to fit.
 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
+    if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
     return std.math.cast(usize, int) catch |err| switch (err) {
         error.Overflow => return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}),
     };