Commit d03c47bf85

Veikka Tuominen <git@vexu.eu>
2022-10-25 23:30:17
Sema: use `runtime_value` instead of creating allocs
1 parent 398a3aa
src/arch/aarch64/CodeGen.zig
@@ -5401,7 +5401,11 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
     }
 }
 
-fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
+fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
+    var typed_value = arg_tv;
+    if (typed_value.val.castTag(.runtime_value)) |rt| {
+        typed_value.val = rt.data;
+    }
     log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
     if (typed_value.val.isUndef())
         return MCValue{ .undef = {} };
src/arch/arm/CodeGen.zig
@@ -6047,7 +6047,11 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
     }
 }
 
-fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
+fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
+    var typed_value = arg_tv;
+    if (typed_value.val.castTag(.runtime_value)) |rt| {
+        typed_value.val = rt.data;
+    }
     log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
     if (typed_value.val.isUndef())
         return MCValue{ .undef = {} };
src/arch/wasm/CodeGen.zig
@@ -2582,7 +2582,11 @@ fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(
     return @intCast(WantedT, result);
 }
 
-fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
+fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
+    var val = arg_val;
+    if (val.castTag(.runtime_value)) |rt| {
+        val = rt.data;
+    }
     if (val.isUndefDeep()) return func.emitUndefined(ty);
     if (val.castTag(.decl_ref)) |decl_ref| {
         const decl_index = decl_ref.data;
src/arch/x86_64/CodeGen.zig
@@ -6960,7 +6960,11 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
     }
 }
 
-fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
+fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
+    var typed_value = arg_tv;
+    if (typed_value.val.castTag(.runtime_value)) |rt| {
+        typed_value.val = rt.data;
+    }
     log.debug("genTypedValue: ty = {}, val = {}", .{ typed_value.ty.fmtDebug(), typed_value.val.fmtDebug() });
     if (typed_value.val.isUndef())
         return MCValue{ .undef = {} };
src/codegen/c.zig
@@ -555,9 +555,13 @@ pub const DeclGen = struct {
         dg: *DeclGen,
         writer: anytype,
         ty: Type,
-        val: Value,
+        arg_val: Value,
         location: ValueRenderLocation,
     ) error{ OutOfMemory, AnalysisFail }!void {
+        var val = arg_val;
+        if (val.castTag(.runtime_value)) |rt| {
+            val = rt.data;
+        }
         const target = dg.module.getTarget();
         if (val.isUndefDeep()) {
             switch (ty.zigTypeTag()) {
src/codegen/llvm.zig
@@ -3187,7 +3187,11 @@ pub const DeclGen = struct {
         return llvm_elem_ty;
     }
 
-    fn lowerValue(dg: *DeclGen, tv: TypedValue) Error!*llvm.Value {
+    fn lowerValue(dg: *DeclGen, arg_tv: TypedValue) Error!*llvm.Value {
+        var tv = arg_tv;
+        if (tv.val.castTag(.runtime_value)) |rt| {
+            tv.val = rt.data;
+        }
         if (tv.val.isUndef()) {
             const llvm_type = try dg.lowerType(tv.ty);
             return llvm_type.getUndef();
src/codegen.zig
@@ -149,7 +149,7 @@ fn writeFloat(comptime F: type, f: F, target: Target, endian: std.builtin.Endian
 pub fn generateSymbol(
     bin_file: *link.File,
     src_loc: Module.SrcLoc,
-    typed_value: TypedValue,
+    arg_tv: TypedValue,
     code: *std.ArrayList(u8),
     debug_output: DebugInfoOutput,
     reloc_info: RelocInfo,
@@ -157,6 +157,11 @@ pub fn generateSymbol(
     const tracy = trace(@src());
     defer tracy.end();
 
+    var typed_value = arg_tv;
+    if (arg_tv.val.castTag(.runtime_value)) |rt| {
+        typed_value.val = rt.data;
+    }
+
     const target = bin_file.options.target;
     const endian = target.cpu.arch.endian();
 
src/Sema.zig
@@ -1827,6 +1827,22 @@ fn resolveMaybeUndefValAllowVariables(
     block: *Block,
     src: LazySrcLoc,
     inst: Air.Inst.Ref,
+) CompileError!?Value {
+    var make_runtime = false;
+    if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, src, inst, &make_runtime)) |val| {
+        if (make_runtime) return null;
+        return val;
+    }
+    return null;
+}
+
+/// Returns all Value tags including `variable`, `undef` and `runtime_value`.
+fn resolveMaybeUndefValAllowVariablesMaybeRuntime(
+    sema: *Sema,
+    block: *Block,
+    src: LazySrcLoc,
+    inst: Air.Inst.Ref,
+    make_runtime: *bool,
 ) CompileError!?Value {
     // First section of indexes correspond to a set number of constant values.
     var i: usize = @enumToInt(inst);
@@ -1843,7 +1859,7 @@ fn resolveMaybeUndefValAllowVariables(
         .constant => {
             const ty_pl = sema.air_instructions.items(.data)[i].ty_pl;
             const val = sema.air_values.items[ty_pl.payload];
-            if (val.tag() == .runtime_int) return null;
+            if (val.tag() == .runtime_value) make_runtime.* = true;
             return val;
         },
         .const_ty => {
@@ -3896,6 +3912,7 @@ fn validateUnionInit(
     var first_block_index = block.instructions.items.len;
     var block_index = block.instructions.items.len - 1;
     var init_val: ?Value = null;
+    var make_runtime = false;
     while (block_index > 0) : (block_index -= 1) {
         const store_inst = block.instructions.items[block_index];
         if (store_inst == field_ptr_air_inst) break;
@@ -3920,7 +3937,7 @@ fn validateUnionInit(
         } else {
             first_block_index = @min(first_block_index, block_index);
         }
-        init_val = try sema.resolveMaybeUndefValAllowVariables(block, init_src, bin_op.rhs);
+        init_val = try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, init_src, bin_op.rhs, &make_runtime);
         break;
     }
 
@@ -3933,10 +3950,11 @@ fn validateUnionInit(
         // instead a single `store` to the result ptr with a comptime union value.
         block.instructions.shrinkRetainingCapacity(first_block_index);
 
-        const union_val = try Value.Tag.@"union".create(sema.arena, .{
+        var union_val = try Value.Tag.@"union".create(sema.arena, .{
             .tag = tag_val,
             .val = val,
         });
+        if (make_runtime) union_val = try Value.Tag.runtime_value.create(sema.arena, union_val);
         const union_init = try sema.addConstant(union_ty, union_val);
         try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store);
         return;
@@ -4054,6 +4072,7 @@ fn validateStructInit(
 
     var struct_is_comptime = true;
     var first_block_index = block.instructions.items.len;
+    var make_runtime = false;
 
     const air_tags = sema.air_instructions.items(.tag);
     const air_datas = sema.air_instructions.items(.data);
@@ -4130,7 +4149,7 @@ fn validateStructInit(
                 } else {
                     first_block_index = @min(first_block_index, block_index);
                 }
-                if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| {
+                if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, field_src, bin_op.rhs, &make_runtime)) |val| {
                     field_values[i] = val;
                 } else {
                     struct_is_comptime = false;
@@ -4185,7 +4204,8 @@ fn validateStructInit(
         // instead a single `store` to the struct_ptr with a comptime struct value.
 
         block.instructions.shrinkRetainingCapacity(first_block_index);
-        const struct_val = try Value.Tag.aggregate.create(sema.arena, field_values);
+        var struct_val = try Value.Tag.aggregate.create(sema.arena, field_values);
+        if (make_runtime) struct_val = try Value.Tag.runtime_value.create(sema.arena, struct_val);
         const struct_init = try sema.addConstant(struct_ty, struct_val);
         try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
         return;
@@ -4265,6 +4285,7 @@ fn zirValidateArrayInit(
 
     var array_is_comptime = true;
     var first_block_index = block.instructions.items.len;
+    var make_runtime = false;
 
     // Collect the comptime element values in case the array literal ends up
     // being comptime-known.
@@ -4326,7 +4347,7 @@ fn zirValidateArrayInit(
                     array_is_comptime = false;
                     continue;
                 }
-                if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| {
+                if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| {
                     element_vals[i] = val;
                 } else {
                     array_is_comptime = false;
@@ -4352,7 +4373,7 @@ fn zirValidateArrayInit(
                     array_is_comptime = false;
                     continue;
                 }
-                if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| {
+                if (try sema.resolveMaybeUndefValAllowVariablesMaybeRuntime(block, elem_src, bin_op.rhs, &make_runtime)) |val| {
                     element_vals[i] = val;
                 } else {
                     array_is_comptime = false;
@@ -4383,7 +4404,8 @@ fn zirValidateArrayInit(
 
         block.instructions.shrinkRetainingCapacity(first_block_index);
 
-        const array_val = try Value.Tag.aggregate.create(sema.arena, element_vals);
+        var array_val = try Value.Tag.aggregate.create(sema.arena, element_vals);
+        if (make_runtime) array_val = try Value.Tag.runtime_value.create(sema.arena, array_val);
         const array_init = try sema.addConstant(array_ty, array_val);
         try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
     }
@@ -6635,20 +6657,14 @@ fn analyzeInlineCallArg(
                     .ty = param_ty,
                     .val = arg_val,
                 };
-            } else if (((try sema.resolveMaybeUndefVal(arg_block, arg_src, casted_arg)) == null) or
-                try sema.typeRequiresComptime(param_ty) or zir_tags[inst] == .param_comptime)
-            {
+            } else if (zir_tags[inst] == .param_comptime or try sema.typeRequiresComptime(param_ty)) {
                 try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg);
-            } else {
+            } else if (try sema.resolveMaybeUndefVal(arg_block, arg_src, casted_arg)) |val| {
                 // We have a comptime value but we need a runtime value to preserve inlining semantics,
-                const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
-                    .pointee_type = param_ty,
-                    .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
-                });
-                const alloc = try arg_block.addTy(.alloc, ptr_type);
-                _ = try arg_block.addBinOp(.store, alloc, casted_arg);
-                const loaded = try arg_block.addTyOp(.load, param_ty, alloc);
-                try sema.inst_map.putNoClobber(sema.gpa, inst, loaded);
+                const wrapped = try sema.addConstant(param_ty, try Value.Tag.runtime_value.create(sema.arena, val));
+                try sema.inst_map.putNoClobber(sema.gpa, inst, wrapped);
+            } else {
+                try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg);
             }
 
             arg_i.* += 1;
@@ -6685,20 +6701,14 @@ fn analyzeInlineCallArg(
                     .ty = sema.typeOf(uncasted_arg),
                     .val = arg_val,
                 };
-            } else if ((try sema.resolveMaybeUndefVal(arg_block, arg_src, uncasted_arg)) == null or
-                try sema.typeRequiresComptime(param_ty) or zir_tags[inst] == .param_anytype_comptime)
-            {
+            } else if (zir_tags[inst] == .param_anytype_comptime or try sema.typeRequiresComptime(param_ty)) {
                 try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg);
-            } else {
+            } else if (try sema.resolveMaybeUndefVal(arg_block, arg_src, uncasted_arg)) |val| {
                 // We have a comptime value but we need a runtime value to preserve inlining semantics,
-                const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
-                    .pointee_type = param_ty,
-                    .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
-                });
-                const alloc = try arg_block.addTy(.alloc, ptr_type);
-                _ = try arg_block.addBinOp(.store, alloc, uncasted_arg);
-                const loaded = try arg_block.addTyOp(.load, param_ty, alloc);
-                try sema.inst_map.putNoClobber(sema.gpa, inst, loaded);
+                const wrapped = try sema.addConstant(param_ty, try Value.Tag.runtime_value.create(sema.arena, val));
+                try sema.inst_map.putNoClobber(sema.gpa, inst, wrapped);
+            } else {
+                try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg);
             }
 
             arg_i.* += 1;
@@ -14826,7 +14836,7 @@ fn zirBuiltinSrc(
     // fn_name: [:0]const u8,
     field_values[1] = func_name_val;
     // line: u32
-    field_values[2] = try Value.Tag.runtime_int.create(sema.arena, extra.line + 1);
+    field_values[2] = try Value.Tag.runtime_value.create(sema.arena, try Value.Tag.int_u64.create(sema.arena, extra.line + 1));
     // column: u32,
     field_values[3] = try Value.Tag.int_u64.create(sema.arena, extra.column + 1);
 
src/TypedValue.zig
@@ -477,6 +477,6 @@ pub fn print(
         },
         .generic_poison_type => return writer.writeAll("(generic poison type)"),
         .generic_poison => return writer.writeAll("(generic poison)"),
-        .runtime_int => return writer.writeAll("[runtime value]"),
+        .runtime_value => return writer.writeAll("[runtime value]"),
     };
 }
src/value.zig
@@ -111,10 +111,12 @@ pub const Value = extern union {
         int_i64,
         int_big_positive,
         int_big_negative,
-        runtime_int,
         function,
         extern_fn,
         variable,
+        /// A wrapper for values which are comptime-known but should
+        /// semantically be runtime-known.
+        runtime_value,
         /// Represents a pointer to a Decl.
         /// When machine codegen backend sees this, it must set the Decl's `alive` field to true.
         decl_ref,
@@ -282,6 +284,7 @@ pub const Value = extern union {
                 .eu_payload,
                 .opt_payload,
                 .empty_array_sentinel,
+                .runtime_value,
                 => Payload.SubValue,
 
                 .eu_payload_ptr,
@@ -305,7 +308,6 @@ pub const Value = extern union {
                 .int_type => Payload.IntType,
                 .int_u64 => Payload.U64,
                 .int_i64 => Payload.I64,
-                .runtime_int => Payload.U64,
                 .function => Payload.Function,
                 .variable => Payload.Variable,
                 .decl_ref_mut => Payload.DeclRefMut,
@@ -485,7 +487,6 @@ pub const Value = extern union {
             },
             .int_type => return self.copyPayloadShallow(arena, Payload.IntType),
             .int_u64 => return self.copyPayloadShallow(arena, Payload.U64),
-            .runtime_int => return self.copyPayloadShallow(arena, Payload.U64),
             .int_i64 => return self.copyPayloadShallow(arena, Payload.I64),
             .int_big_positive, .int_big_negative => {
                 const old_payload = self.cast(Payload.BigInt).?;
@@ -567,6 +568,7 @@ pub const Value = extern union {
             .eu_payload,
             .opt_payload,
             .empty_array_sentinel,
+            .runtime_value,
             => {
                 const payload = self.cast(Payload.SubValue).?;
                 const new_payload = try arena.create(Payload.SubValue);
@@ -765,7 +767,7 @@ pub const Value = extern union {
             .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),
             .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),
             .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),
-            .runtime_int => return out_stream.writeAll("[runtime value]"),
+            .runtime_value => return out_stream.writeAll("[runtime value]"),
             .function => return out_stream.print("(function decl={d})", .{val.castTag(.function).?.data.owner_decl}),
             .extern_fn => return out_stream.writeAll("(extern function)"),
             .variable => return out_stream.writeAll("(variable)"),
@@ -1081,8 +1083,6 @@ pub const Value = extern union {
             .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt(),
             .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt(),
 
-            .runtime_int => return BigIntMutable.init(&space.limbs, val.castTag(.runtime_int).?.data).toConst(),
-
             .undef => unreachable,
 
             .lazy_align => {
@@ -1138,8 +1138,6 @@ pub const Value = extern union {
             .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(u64) catch null,
             .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(u64) catch null,
 
-            .runtime_int => return val.castTag(.runtime_int).?.data,
-
             .undef => unreachable,
 
             .lazy_align => {
@@ -2357,6 +2355,8 @@ pub const Value = extern union {
         const zig_ty_tag = ty.zigTypeTag();
         std.hash.autoHash(hasher, zig_ty_tag);
         if (val.isUndef()) return;
+        // The value is runtime-known and shouldn't affect the hash.
+        if (val.tag() == .runtime_value) return;
 
         switch (zig_ty_tag) {
             .BoundFn => unreachable, // TODO remove this from the language
@@ -2632,9 +2632,6 @@ pub const Value = extern union {
             .lazy_size,
             => return hashInt(ptr_val, hasher, target),
 
-            // The value is runtime-known and shouldn't affect the hash.
-            .runtime_int => {},
-
             else => unreachable,
         }
     }
test/behavior/bugs/13164.zig
@@ -10,6 +10,7 @@ inline fn setLimits(min: ?u32, max: ?u32) !void {
 test {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
 
     var x: u32 = 42;
     try setLimits(x, null);
test/behavior/vector.zig
@@ -1135,3 +1135,40 @@ test "array of vectors is copied" {
     points2[0..points.len].* = points;
     try std.testing.expectEqual(points2[6], Vec3{ -345, -311, 381 });
 }
+
+test "byte vector initialized in inline function" {
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    const S = struct {
+        inline fn boolx4(e0: bool, e1: bool, e2: bool, e3: bool) @Vector(4, bool) {
+            return .{ e0, e1, e2, e3 };
+        }
+
+        fn all(vb: @Vector(4, bool)) bool {
+            return @reduce(.And, vb);
+        }
+    };
+
+    try expect(S.all(S.boolx4(true, true, true, true)));
+}
+
+test "byte vector initialized in inline function" {
+    // TODO https://github.com/ziglang/zig/issues/13279
+    if (true) return error.SkipZigTest;
+
+    const S = struct {
+        fn boolx4(e0: bool, e1: bool, e2: bool, e3: bool) @Vector(4, bool) {
+            return .{ e0, e1, e2, e3 };
+        }
+
+        fn all(vb: @Vector(4, bool)) bool {
+            return @reduce(.And, vb);
+        }
+    };
+
+    try expect(S.all(S.boolx4(true, true, true, true)));
+}