authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-04 21:59:01-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-04 21:59:01-05:00
log70ad5bc3631c330eb41b9379d7e93f0647da2fd6
tree8b9e7c78245891efee3cc8b4f0e3ad770704bb7e
parentbdb6fb57639a4508dda523301c5eaf31e8d89edf
parent7d3cc3bc8d772519d390b7f13346eeab73bc0c21
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13768 from ziglang/cbe-reuse-locals-2

C backend: reuse locals but respect loops to pass behavior tests

10 files changed, 1690 insertions(+), 832 deletions(-)

build.sh created+9
......@@ -0,0 +1,9 @@
1#!/bin/sh
2set -e
3if [ "x$1" != x--debug ]; then
4 cmake -GNinja -S. -Bbuild -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER:FILEPATH=clang -DCMAKE_CXX_COMPILER:FILEPATH=clang++ -DZIG_NO_LIB:BOOL=ON
5 cmake --build build
6 cmake --install build
7fi
8build/stage3/bin/zig build -p debug -Dno-lib -Denable-stage1 -Denable-llvm -freference-trace
9#build/stage3/bin/zig build -p only-c -Dno-lib -Donly-c
lib/std/array_hash_map.zig+19
......@@ -399,6 +399,14 @@ pub fn ArrayHashMap(
399399 return other.promoteContext(allocator, ctx);
400400 }
401401
402 /// Set the map to an empty state, making deinitialization a no-op, and
403 /// returning a copy of the original.
404 pub fn move(self: *Self) Self {
405 const result = self.*;
406 self.unmanaged = .{};
407 return result;
408 }
409
402410 /// Rebuilds the key indexes. If the underlying entries has been modified directly, users
403411 /// can call `reIndex` to update the indexes to account for these new entries.
404412 pub fn reIndex(self: *Self) !void {
......@@ -1149,6 +1157,8 @@ pub fn ArrayHashMapUnmanaged(
11491157 errdefer other.entries.deinit(allocator);
11501158
11511159 if (self.index_header) |header| {
1160 // TODO: I'm pretty sure this could be memcpy'd instead of
1161 // doing all this work.
11521162 const new_header = try IndexHeader.alloc(allocator, header.bit_index);
11531163 other.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header);
11541164 other.index_header = new_header;
......@@ -1156,6 +1166,14 @@ pub fn ArrayHashMapUnmanaged(
11561166 return other;
11571167 }
11581168
1169 /// Set the map to an empty state, making deinitialization a no-op, and
1170 /// returning a copy of the original.
1171 pub fn move(self: *Self) Self {
1172 const result = self.*;
1173 self.* = .{};
1174 return result;
1175 }
1176
11591177 /// Rebuilds the key indexes. If the underlying entries has been modified directly, users
11601178 /// can call `reIndex` to update the indexes to account for these new entries.
11611179 pub fn reIndex(self: *Self, allocator: Allocator) !void {
......@@ -1163,6 +1181,7 @@ pub fn ArrayHashMapUnmanaged(
11631181 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call reIndexContext instead.");
11641182 return self.reIndexContext(allocator, undefined);
11651183 }
1184
11661185 pub fn reIndexContext(self: *Self, allocator: Allocator, ctx: Context) !void {
11671186 if (self.entries.capacity <= linear_scan_max) return;
11681187 // We're going to rebuild the index header and replace the existing one (if any). The
lib/std/hash_map.zig+16
......@@ -673,6 +673,14 @@ pub fn HashMap(
673673 var other = try self.unmanaged.cloneContext(new_allocator, new_ctx);
674674 return other.promoteContext(new_allocator, new_ctx);
675675 }
676
677 /// Set the map to an empty state, making deinitialization a no-op, and
678 /// returning a copy of the original.
679 pub fn move(self: *Self) Self {
680 const result = self.*;
681 self.unmanaged = .{};
682 return result;
683 }
676684 };
677685}
678686
......@@ -1488,6 +1496,14 @@ pub fn HashMapUnmanaged(
14881496 return other;
14891497 }
14901498
1499 /// Set the map to an empty state, making deinitialization a no-op, and
1500 /// returning a copy of the original.
1501 pub fn move(self: *Self) Self {
1502 const result = self.*;
1503 self.* = .{};
1504 return result;
1505 }
1506
14911507 fn grow(self: *Self, allocator: Allocator, new_capacity: Size, ctx: Context) Allocator.Error!void {
14921508 @setCold(true);
14931509 const new_cap = std.math.max(new_capacity, minimal_capacity);
src/arch/wasm/CodeGen.zig+8-6
......@@ -2001,7 +2001,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20012001
20022002 try func.restoreStackPointer();
20032003 try func.addTag(.@"return");
2004 return func.finishAir(inst, .none, &.{});
2004 return func.finishAir(inst, .none, &.{un_op});
20052005}
20062006
20072007fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) InnerError!void {
......@@ -3161,7 +3161,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
31613161 }
31623162 break :result func.reuseOperand(ty_op.operand, operand);
31633163 } else WValue{ .none = {} };
3164 func.finishAir(inst, result, &.{});
3164 func.finishAir(inst, result, &.{ty_op.operand});
31653165}
31663166
31673167fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue {
......@@ -4115,7 +4115,7 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41154115 const len = try func.resolveInst(bin_op.rhs);
41164116 try func.memset(ptr, len, value);
41174117
4118 func.finishAir(inst, .none, &.{pl_op.operand});
4118 func.finishAir(inst, .none, &.{ pl_op.operand, bin_op.lhs, bin_op.rhs });
41194119}
41204120
41214121/// Sets a region of memory at `ptr` to the value of `value`
......@@ -4424,6 +4424,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
44244424 else => unreachable,
44254425 }
44264426 };
4427 // TODO: this is incorrect Liveness handling code
44274428 func.finishAir(inst, result, &.{});
44284429}
44294430
......@@ -4747,7 +4748,7 @@ fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
47474748 const len = try func.resolveInst(bin_op.rhs);
47484749 try func.memcpy(dst, src, len);
47494750
4750 func.finishAir(inst, .none, &.{pl_op.operand});
4751 func.finishAir(inst, .none, &.{ pl_op.operand, bin_op.lhs, bin_op.rhs });
47514752}
47524753
47534754fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
......@@ -5158,7 +5159,8 @@ fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerE
51585159fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51595160 const pl_op = func.air.instructions.items(.data)[inst].pl_op;
51605161 const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data;
5161 if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs });
5162 if (func.liveness.isUnused(inst))
5163 return func.finishAir(inst, .none, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
51625164
51635165 const ty = func.air.typeOfIndex(inst);
51645166 if (ty.zigTypeTag() == .Vector) {
......@@ -5186,7 +5188,7 @@ fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
51865188 break :result try (try func.binOp(mul_result, addend, ty, .add)).toLocal(func, ty);
51875189 };
51885190
5189 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
5191 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
51905192}
51915193
51925194fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
src/codegen/c.zig+1604-814
......@@ -30,10 +30,9 @@ const BigInt = std.math.big.int;
3030
3131pub const CValue = union(enum) {
3232 none: void,
33 /// Index into local_names
34 local: usize,
35 /// Index into local_names, but take the address.
36 local_ref: usize,
33 local: LocalIndex,
34 /// Address of a local.
35 local_ref: LocalIndex,
3736 /// A constant instruction, to be rendered inline.
3837 constant: Air.Inst.Ref,
3938 /// Index into the parameters
......@@ -50,8 +49,6 @@ pub const CValue = union(enum) {
5049 /// Render these bytes literally.
5150 /// TODO make this a [*:0]const u8 to save memory
5251 bytes: []const u8,
53 /// Index of an instruction that should later be rendered inline.
54 inline_index: Air.Inst.Index,
5552};
5653
5754const BlockData = struct {
......@@ -72,6 +69,19 @@ pub const TypedefMap = std.ArrayHashMap(
7269 true,
7370);
7471
72const LoopDepth = u16;
73const Local = struct {
74 ty: Type,
75 alignment: u32,
76 /// How many loops the last definition was nested in.
77 loop_depth: LoopDepth,
78};
79
80const LocalIndex = u16;
81const LocalsList = std.ArrayListUnmanaged(LocalIndex);
82const LocalsMap = std.ArrayHashMapUnmanaged(Type, LocalsList, Type.HashContext32, true);
83const LocalsStack = std.ArrayListUnmanaged(LocalsMap);
84
7585const FormatTypeAsCIdentContext = struct {
7686 ty: Type,
7787 mod: *Module,
......@@ -81,7 +91,6 @@ const ValueRenderLocation = enum {
8191 FunctionArgument,
8292 Initializer,
8393 Other,
84 condition,
8594};
8695
8796const BuiltinInfo = enum {
......@@ -254,10 +263,29 @@ pub const Function = struct {
254263 value_map: CValueMap,
255264 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
256265 next_arg_index: usize = 0,
257 next_local_index: usize = 0,
258266 next_block_index: usize = 0,
259267 object: Object,
260268 func: *Module.Fn,
269 /// All the locals, to be emitted at the top of the function.
270 locals: std.ArrayListUnmanaged(Local) = .{},
271 /// Which locals are available for reuse, based on Type.
272 /// Only locals in the last stack entry are available for reuse,
273 /// other entries will become available on loop exit.
274 free_locals_stack: LocalsStack = .{},
275 free_locals_clone_depth: LoopDepth = 0,
276 /// Locals which will not be freed by Liveness. This is used after a
277 /// Function body is lowered in order to make `free_locals_stack` have
278 /// 100% of the locals within so that it can be used to render the block
279 /// of variable declarations at the top of a function, sorted descending
280 /// by type alignment.
281 /// The value is whether the alloc is static or not.
282 allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .{},
283 /// Needed for memory used by the keys of free_locals_stack entries.
284 arena: std.heap.ArenaAllocator,
285
286 fn tyHashCtx(f: Function) Type.HashContext32 {
287 return .{ .mod = f.object.dg.module };
288 }
261289
262290 fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue {
263291 const gop = try f.value_map.getOrPut(inst);
......@@ -268,9 +296,12 @@ pub const Function = struct {
268296
269297 const result = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: {
270298 const writer = f.object.code_header.writer();
271 const decl_c_value = f.allocLocalValue();
299 const alignment = 0;
300 const decl_c_value = try f.allocLocalValue(ty, alignment);
301 const gpa = f.object.dg.gpa;
302 try f.allocs.put(gpa, decl_c_value.local, true);
272303 try writer.writeAll("static ");
273 try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, 0, .Complete);
304 try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, alignment, .Complete);
274305 try writer.writeAll(" = ");
275306 try f.object.dg.renderValue(writer, ty, val, .Initializer);
276307 try writer.writeAll(";\n ");
......@@ -281,19 +312,6 @@ pub const Function = struct {
281312 return result;
282313 }
283314
284 fn resolveInstNoInline(f: *Function, inst: Air.Inst.Ref) !CValue {
285 const operand = try f.resolveInst(inst);
286 if (operand != .inline_index) return operand;
287
288 const inst_ty = f.air.typeOf(inst);
289 const writer = f.object.writer();
290 const local = try f.allocLocal(inst_ty, .Const);
291 try writer.writeAll(" = ");
292 try f.writeCValueInline(operand.inline_index);
293 try writer.writeAll(";\n");
294 return local;
295 }
296
297315 fn wantSafety(f: *Function) bool {
298316 return switch (f.object.dg.module.optimizeMode()) {
299317 .Debug, .ReleaseSafe => true,
......@@ -301,27 +319,43 @@ pub const Function = struct {
301319 };
302320 }
303321
304 fn allocLocalValue(f: *Function) CValue {
305 const result = f.next_local_index;
306 f.next_local_index += 1;
307 return .{ .local = result };
322 fn getFreeLocals(f: *Function) *LocalsMap {
323 return &f.free_locals_stack.items[f.free_locals_stack.items.len - 1];
324 }
325
326 /// Skips the reuse logic.
327 fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue {
328 const gpa = f.object.dg.gpa;
329 try f.locals.append(gpa, .{
330 .ty = ty,
331 .alignment = alignment,
332 .loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1),
333 });
334 return CValue{ .local = @intCast(LocalIndex, f.locals.items.len - 1) };
308335 }
309336
310 fn allocLocal(f: *Function, ty: Type, mutability: Mutability) !CValue {
311 return f.allocAlignedLocal(ty, mutability, 0);
337 fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue {
338 const result = try f.allocAlignedLocal(ty, .Mut, 0);
339 log.debug("%{d}: allocating t{d}", .{ inst, result.local });
340 return result;
312341 }
313342
343 /// Only allocates the local; does not print anything.
314344 fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: u32) !CValue {
315 const local_value = f.allocLocalValue();
316 try f.object.dg.renderTypeAndName(
317 f.object.writer(),
318 ty,
319 local_value,
320 mutability,
321 alignment,
322 .Complete,
323 );
324 return local_value;
345 _ = mutability;
346
347 if (f.getFreeLocals().getPtrContext(ty, f.tyHashCtx())) |locals_list| {
348 for (locals_list.items) |local_index, i| {
349 const local = &f.locals.items[local_index];
350 if (local.alignment >= alignment) {
351 local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1);
352 _ = locals_list.swapRemove(i);
353 return CValue{ .local = local_index };
354 }
355 }
356 }
357
358 return try f.allocLocalValue(ty, alignment);
325359 }
326360
327361 fn writeCValue(f: *Function, w: anytype, c_value: CValue, location: ValueRenderLocation) !void {
......@@ -329,95 +363,10 @@ pub const Function = struct {
329363 .constant => |inst| {
330364 const ty = f.air.typeOf(inst);
331365 const val = f.air.value(inst).?;
332 try f.object.dg.renderValue(w, ty, val, location);
333 },
334 .undef => |ty| try f.object.dg.renderValue(w, ty, Value.undef, location),
335 .inline_index => |node| {
336 if (location != .condition) try w.writeByte('(');
337 try f.writeCValueInline(node);
338 if (location != .condition) try w.writeByte(')');
339 },
340 else => try f.object.dg.writeCValue(w, c_value),
341 }
342 }
343
344 const E = error{ OutOfMemory, AnalysisFail };
345
346 fn writeCValueInline(f: *Function, inst: Air.Inst.Index) E!void {
347 switch (f.air.instructions.items(.tag)[inst]) {
348 // zig fmt: off
349 // TODO use a different strategy for add, sub, mul, div
350 // that communicates to the optimizer that wrapping is UB.
351 .add => try airBinOp(f, inst, "+", "add", .None),
352 .sub => try airBinOp(f, inst, "-", "sub", .None),
353 .mul => try airBinOp(f, inst, "*", "mul", .None),
354
355 .div_float => try airBinBuiltinCall(f, inst, "div", .None),
356
357 .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None),
358 .rem => {
359 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
360 const lhs_ty = f.air.typeOf(bin_op.lhs);
361 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),
362 // so we only check one.
363 if (lhs_ty.isInt())
364 try airBinOp(f, inst, "%", "rem", .None)
365 else
366 try airBinFloatOp(f, inst, "fmod");
366 return f.object.dg.renderValue(w, ty, val, location);
367367 },
368 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None),
369 .mod => try airBinBuiltinCall(f, inst, "mod", .None),
370
371 .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits),
372 .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits),
373 .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits),
374
375 .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits),
376 .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits),
377 .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits),
378 .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits),
379
380 .min => try airMinMax(f, inst, '<', "fmin"),
381 .max => try airMinMax(f, inst, '>', "fmax"),
382
383 .cmp_gt => try airCmpOp(f, inst, ">", "gt"),
384 .cmp_gte => try airCmpOp(f, inst, ">=", "ge"),
385 .cmp_lt => try airCmpOp(f, inst, "<", "lt"),
386 .cmp_lte => try airCmpOp(f, inst, "<=", "le"),
387
388 .cmp_eq => try airEquality(f, inst, "((", "==", "eq"),
389 .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"),
390
391 .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None),
392 .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None),
393 .xor => try airBinOp(f, inst, "^", "xor", .None),
394 .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None),
395 .shl, => try airBinBuiltinCall(f, inst, "shlw", .Bits),
396 .shl_exact => try airBinOp(f, inst, "<<", "shl", .None),
397 .not => try airNot (f, inst),
398
399 .is_err => try airIsErr(f, inst, false, "!="),
400 .is_non_err => try airIsErr(f, inst, false, "=="),
401 .is_err_ptr => try airIsErr(f, inst, true, "!="),
402 .is_non_err_ptr => try airIsErr(f, inst, true, "=="),
403
404 .is_null => try airIsNull(f, inst, "==", false),
405 .is_non_null => try airIsNull(f, inst, "!=", false),
406 .is_null_ptr => try airIsNull(f, inst, "==", true),
407 .is_non_null_ptr => try airIsNull(f, inst, "!=", true),
408
409 .get_union_tag => try airGetUnionTag(f, inst),
410 .clz => try airUnBuiltinCall(f, inst, "clz", .Bits),
411 .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits),
412 .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits),
413 .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits),
414 .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits),
415 .tag_name => try airTagName(f, inst),
416 .error_name => try airErrorName(f, inst),
417
418 .ptrtoint => try airPtrToInt(f, inst),
419 else => unreachable,
420 // zig fmt: on
368 .undef => |ty| return f.object.dg.renderValue(w, ty, Value.undef, location),
369 else => return f.object.dg.writeCValue(w, c_value),
421370 }
422371 }
423372
......@@ -443,7 +392,6 @@ pub const Function = struct {
443392 try w.writeByte('.');
444393 return f.writeCValue(w, member, .Other);
445394 },
446 .inline_index => unreachable, // Use resolveInstNoInline
447395 else => return f.object.dg.writeCValueMember(w, c_value, member),
448396 }
449397 }
......@@ -477,6 +425,24 @@ pub const Function = struct {
477425 fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) {
478426 return f.object.dg.fmtIntLiteral(ty, val);
479427 }
428
429 pub fn deinit(f: *Function, gpa: mem.Allocator) void {
430 f.allocs.deinit(gpa);
431 f.locals.deinit(gpa);
432 for (f.free_locals_stack.items) |*free_locals| {
433 deinitFreeLocalsMap(gpa, free_locals);
434 }
435 f.free_locals_stack.deinit(gpa);
436 f.blocks.deinit(gpa);
437 f.value_map.deinit();
438 f.object.code.deinit();
439 for (f.object.dg.typedefs.values()) |typedef| {
440 gpa.free(typedef.rendered);
441 }
442 f.object.dg.typedefs.deinit();
443 f.object.dg.fwd_decl.deinit();
444 f.arena.deinit();
445 }
480446};
481447
482448/// This data is available when outputting .c code for a `Module`.
......@@ -2267,7 +2233,7 @@ pub const DeclGen = struct {
22672233
22682234 fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void {
22692235 switch (c_value) {
2270 .none, .inline_index => unreachable,
2236 .none => unreachable,
22712237 .local => |i| return w.print("t{d}", .{i}),
22722238 .local_ref => |i| return w.print("&t{d}", .{i}),
22732239 .constant => unreachable,
......@@ -2286,7 +2252,7 @@ pub const DeclGen = struct {
22862252
22872253 fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void {
22882254 switch (c_value) {
2289 .none, .inline_index => unreachable,
2255 .none => unreachable,
22902256 .local => |i| return w.print("(*t{d})", .{i}),
22912257 .local_ref => |i| return w.print("t{d}", .{i}),
22922258 .constant => unreachable,
......@@ -2316,7 +2282,7 @@ pub const DeclGen = struct {
23162282
23172283 fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void {
23182284 switch (c_value) {
2319 .none, .constant, .field, .undef, .inline_index => unreachable,
2285 .none, .constant, .field, .undef => unreachable,
23202286 .local, .arg, .decl, .identifier, .bytes => {
23212287 try dg.writeCValue(writer, c_value);
23222288 try writer.writeAll("->");
......@@ -2502,12 +2468,13 @@ pub fn genFunc(f: *Function) !void {
25022468 defer tracy.end();
25032469
25042470 const o = &f.object;
2471 const gpa = o.dg.gpa;
25052472 const tv: TypedValue = .{
25062473 .ty = o.dg.decl.ty,
25072474 .val = o.dg.decl.val,
25082475 };
25092476
2510 o.code_header = std.ArrayList(u8).init(f.object.dg.gpa);
2477 o.code_header = std.ArrayList(u8).init(gpa);
25112478 defer o.code_header.deinit();
25122479
25132480 const is_global = o.dg.declIsGlobal(tv);
......@@ -2529,11 +2496,59 @@ pub fn genFunc(f: *Function) !void {
25292496 o.code_header.appendSliceAssumeCapacity("{\n ");
25302497 const empty_header_len = o.code_header.items.len;
25312498
2499 f.free_locals_stack.clearRetainingCapacity();
2500 try f.free_locals_stack.append(gpa, .{});
2501
25322502 const main_body = f.air.getMainBody();
25332503 try genBody(f, main_body);
25342504
25352505 try o.indent_writer.insertNewline();
25362506
2507 // Take advantage of the free_locals map to bucket locals per type. All
2508 // locals corresponding to AIR instructions should be in there due to
2509 // Liveness analysis, however, locals from alloc instructions will be
2510 // missing. These are added now to complete the map. Then we can sort by
2511 // alignment, descending.
2512 const free_locals = f.getFreeLocals();
2513 const values = f.allocs.values();
2514 for (f.allocs.keys()) |local_index, i| {
2515 if (values[i]) continue; // static
2516 const local = f.locals.items[local_index];
2517 log.debug("inserting local {d} into free_locals", .{local_index});
2518 const gop = try free_locals.getOrPutContext(gpa, local.ty, f.tyHashCtx());
2519 if (!gop.found_existing) gop.value_ptr.* = .{};
2520 try gop.value_ptr.append(gpa, local_index);
2521 }
2522
2523 const SortContext = struct {
2524 target: std.Target,
2525 keys: []const Type,
2526
2527 pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
2528 const a_ty = ctx.keys[a_index];
2529 const b_ty = ctx.keys[b_index];
2530 return b_ty.abiAlignment(ctx.target) < a_ty.abiAlignment(ctx.target);
2531 }
2532 };
2533 const target = o.dg.module.getTarget();
2534 free_locals.sort(SortContext{ .target = target, .keys = free_locals.keys() });
2535
2536 const w = o.code_header.writer();
2537 for (free_locals.values()) |list| {
2538 for (list.items) |local_index| {
2539 const local = f.locals.items[local_index];
2540 try o.dg.renderTypeAndName(
2541 w,
2542 local.ty,
2543 .{ .local = local_index },
2544 .Mut,
2545 local.alignment,
2546 .Complete,
2547 );
2548 try w.writeAll(";\n ");
2549 }
2550 }
2551
25372552 // If we have a header to insert, append the body to the header
25382553 // and then return the result, freeing the body.
25392554 if (o.code_header.items.len > empty_header_len) {
......@@ -2655,41 +2670,51 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
26552670 .ptr_add => try airPtrAddSub(f, inst, '+'),
26562671 .ptr_sub => try airPtrAddSub(f, inst, '-'),
26572672
2658 .add => CValue{ .inline_index = inst },
2659 .sub => CValue{ .inline_index = inst },
2660 .mul => CValue{ .inline_index = inst },
2673 // TODO use a different strategy for add, sub, mul, div
2674 // that communicates to the optimizer that wrapping is UB.
2675 .add => try airBinOp(f, inst, "+", "add", .None),
2676 .sub => try airBinOp(f, inst, "-", "sub", .None),
2677 .mul => try airBinOp(f, inst, "*", "mul", .None),
26612678
26622679 .neg => try airFloatNeg(f, inst),
2663 .div_float => CValue{ .inline_index = inst },
2664
2665 .div_trunc, .div_exact => CValue{ .inline_index = inst },
2666 .rem => CValue{ .inline_index = inst },
2667 .div_floor => CValue{ .inline_index = inst },
2668 .mod => CValue{ .inline_index = inst },
2669
2670 .addwrap => CValue{ .inline_index = inst },
2671 .subwrap => CValue{ .inline_index = inst },
2672 .mulwrap => CValue{ .inline_index = inst },
2673
2674 .add_sat => CValue{ .inline_index = inst },
2675 .sub_sat => CValue{ .inline_index = inst },
2676 .mul_sat => CValue{ .inline_index = inst },
2677 .shl_sat => CValue{ .inline_index = inst },
2678
2679 .sqrt,
2680 .sin,
2681 .cos,
2682 .tan,
2683 .exp,
2684 .exp2,
2685 .log,
2686 .log2,
2687 .log10,
2688 .fabs,
2689 .floor,
2690 .ceil,
2691 .round,
2692 => |tag| try airUnFloatOp(f, inst, @tagName(tag)),
2680 .div_float => try airBinBuiltinCall(f, inst, "div", .None),
2681
2682 .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None),
2683 .rem => blk: {
2684 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
2685 const lhs_ty = f.air.typeOf(bin_op.lhs);
2686 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),
2687 // so we only check one.
2688 break :blk if (lhs_ty.isInt())
2689 try airBinOp(f, inst, "%", "rem", .None)
2690 else
2691 try airBinFloatOp(f, inst, "fmod");
2692 },
2693 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None),
2694 .mod => try airBinBuiltinCall(f, inst, "mod", .None),
2695
2696 .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits),
2697 .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits),
2698 .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits),
2699
2700 .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits),
2701 .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits),
2702 .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits),
2703 .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits),
2704
2705 .sqrt => try airUnFloatOp(f, inst, "sqrt"),
2706 .sin => try airUnFloatOp(f, inst, "sin"),
2707 .cos => try airUnFloatOp(f, inst, "cos"),
2708 .tan => try airUnFloatOp(f, inst, "tan"),
2709 .exp => try airUnFloatOp(f, inst, "exp"),
2710 .exp2 => try airUnFloatOp(f, inst, "exp2"),
2711 .log => try airUnFloatOp(f, inst, "log"),
2712 .log2 => try airUnFloatOp(f, inst, "log2"),
2713 .log10 => try airUnFloatOp(f, inst, "log10"),
2714 .fabs => try airUnFloatOp(f, inst, "fabs"),
2715 .floor => try airUnFloatOp(f, inst, "floor"),
2716 .ceil => try airUnFloatOp(f, inst, "ceil"),
2717 .round => try airUnFloatOp(f, inst, "round"),
26932718 .trunc_float => try airUnFloatOp(f, inst, "trunc"),
26942719
26952720 .mul_add => try airMulAdd(f, inst),
......@@ -2699,45 +2724,45 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
26992724 .mul_with_overflow => try airOverflow(f, inst, "mul", .Bits),
27002725 .shl_with_overflow => try airOverflow(f, inst, "shl", .Bits),
27012726
2702 .min => CValue{ .inline_index = inst },
2703 .max => CValue{ .inline_index = inst },
2727 .min => try airMinMax(f, inst, '<', "fmin"),
2728 .max => try airMinMax(f, inst, '>', "fmax"),
27042729
27052730 .slice => try airSlice(f, inst),
27062731
2707 .cmp_gt => CValue{ .inline_index = inst },
2708 .cmp_gte => CValue{ .inline_index = inst },
2709 .cmp_lt => CValue{ .inline_index = inst },
2710 .cmp_lte => CValue{ .inline_index = inst },
2732 .cmp_gt => try airCmpOp(f, inst, ">", "gt"),
2733 .cmp_gte => try airCmpOp(f, inst, ">=", "ge"),
2734 .cmp_lt => try airCmpOp(f, inst, "<", "lt"),
2735 .cmp_lte => try airCmpOp(f, inst, "<=", "le"),
27112736
2712 .cmp_eq => CValue{ .inline_index = inst },
2713 .cmp_neq => CValue{ .inline_index = inst },
2737 .cmp_eq => try airEquality(f, inst, "((", "==", "eq"),
2738 .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"),
27142739
27152740 .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}),
27162741 .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst),
27172742
27182743 // bool_and and bool_or are non-short-circuit operations
2719 .bool_and, .bit_and => CValue{ .inline_index = inst },
2720 .bool_or, .bit_or => CValue{ .inline_index = inst },
2721 .xor => CValue{ .inline_index = inst },
2722 .shr, .shr_exact => CValue{ .inline_index = inst },
2723 .shl, => CValue{ .inline_index = inst },
2724 .shl_exact => CValue{ .inline_index = inst },
2725 .not => CValue{ .inline_index = inst },
2744 .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .None),
2745 .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .None),
2746 .xor => try airBinOp(f, inst, "^", "xor", .None),
2747 .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .None),
2748 .shl, => try airBinBuiltinCall(f, inst, "shlw", .Bits),
2749 .shl_exact => try airBinOp(f, inst, "<<", "shl", .None),
2750 .not => try airNot (f, inst),
27262751
27272752 .optional_payload => try airOptionalPayload(f, inst),
27282753 .optional_payload_ptr => try airOptionalPayloadPtr(f, inst),
27292754 .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst),
27302755 .wrap_optional => try airWrapOptional(f, inst),
27312756
2732 .is_err => CValue{ .inline_index = inst },
2733 .is_non_err => CValue{ .inline_index = inst },
2734 .is_err_ptr => CValue{ .inline_index = inst },
2735 .is_non_err_ptr => CValue{ .inline_index = inst },
2757 .is_err => try airIsErr(f, inst, false, "!="),
2758 .is_non_err => try airIsErr(f, inst, false, "=="),
2759 .is_err_ptr => try airIsErr(f, inst, true, "!="),
2760 .is_non_err_ptr => try airIsErr(f, inst, true, "=="),
27362761
2737 .is_null => CValue{ .inline_index = inst },
2738 .is_non_null => CValue{ .inline_index = inst },
2739 .is_null_ptr => CValue{ .inline_index = inst },
2740 .is_non_null_ptr => CValue{ .inline_index = inst },
2762 .is_null => try airIsNull(f, inst, "==", false),
2763 .is_non_null => try airIsNull(f, inst, "!=", false),
2764 .is_null_ptr => try airIsNull(f, inst, "==", true),
2765 .is_non_null_ptr => try airIsNull(f, inst, "!=", true),
27412766
27422767 .alloc => try airAlloc(f, inst),
27432768 .ret_ptr => try airRetPtr(f, inst),
......@@ -2765,14 +2790,14 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
27652790 .memset => try airMemset(f, inst),
27662791 .memcpy => try airMemcpy(f, inst),
27672792 .set_union_tag => try airSetUnionTag(f, inst),
2768 .get_union_tag => CValue{ .inline_index = inst },
2769 .clz => CValue{ .inline_index = inst },
2770 .ctz => CValue{ .inline_index = inst },
2771 .popcount => CValue{ .inline_index = inst },
2772 .byte_swap => CValue{ .inline_index = inst },
2773 .bit_reverse => CValue{ .inline_index = inst },
2774 .tag_name => CValue{ .inline_index = inst },
2775 .error_name => CValue{ .inline_index = inst },
2793 .get_union_tag => try airGetUnionTag(f, inst),
2794 .clz => try airUnBuiltinCall(f, inst, "clz", .Bits),
2795 .ctz => try airUnBuiltinCall(f, inst, "ctz", .Bits),
2796 .popcount => try airUnBuiltinCall(f, inst, "popcount", .Bits),
2797 .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .Bits),
2798 .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits),
2799 .tag_name => try airTagName(f, inst),
2800 .error_name => try airErrorName(f, inst),
27762801 .splat => try airSplat(f, inst),
27772802 .select => try airSelect(f, inst),
27782803 .shuffle => try airShuffle(f, inst),
......@@ -2808,7 +2833,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
28082833 .fpext,
28092834 => try airFloatCast(f, inst),
28102835
2811 .ptrtoint => CValue{ .inline_index = inst },
2836 .ptrtoint => try airPtrToInt(f, inst),
28122837
28132838 .atomic_store_unordered => try airAtomicStore(f, inst, toMemoryOrder(.Unordered)),
28142839 .atomic_store_monotonic => try airAtomicStore(f, inst, toMemoryOrder(.Monotonic)),
......@@ -2877,6 +2902,9 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
28772902 .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}),
28782903 // zig fmt: on
28792904 };
2905 if (result_value == .local) {
2906 log.debug("map %{d} to t{d}", .{ inst, result_value.local });
2907 }
28802908 switch (result_value) {
28812909 .none => {},
28822910 else => try f.value_map.putNoClobber(Air.indexToRef(inst), result_value),
......@@ -2885,13 +2913,19 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
28852913}
28862914
28872915fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: []const u8) !CValue {
2888 if (f.liveness.isUnused(inst)) return CValue.none;
2916 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
2917
2918 if (f.liveness.isUnused(inst)) {
2919 try reap(f, inst, &.{ty_op.operand});
2920 return CValue.none;
2921 }
28892922
28902923 const inst_ty = f.air.typeOfIndex(inst);
2891 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
2892 const operand = try f.resolveInstNoInline(ty_op.operand);
2924 const operand = try f.resolveInst(ty_op.operand);
2925 try reap(f, inst, &.{ty_op.operand});
28932926 const writer = f.object.writer();
2894 const local = try f.allocLocal(inst_ty, .Const);
2927 const local = try f.allocLocal(inst, inst_ty);
2928 try f.writeCValue(writer, local, .Other);
28952929 try writer.writeAll(" = ");
28962930 if (is_ptr) {
28972931 try writer.writeByte('&');
......@@ -2906,22 +2940,29 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
29062940 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
29072941 const ptr_ty = f.air.typeOf(bin_op.lhs);
29082942 if ((!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or
2909 !inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
2943 !inst_ty.hasRuntimeBitsIgnoreComptime())
2944 {
2945 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
2946 return CValue.none;
2947 }
29102948
29112949 const ptr = try f.resolveInst(bin_op.lhs);
29122950 const index = try f.resolveInst(bin_op.rhs);
2951 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
29132952
29142953 const target = f.object.dg.module.getTarget();
29152954 const is_array = lowersToArray(inst_ty, target);
29162955
2917 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
2956 const local = try f.allocLocal(inst, inst_ty);
29182957 const writer = f.object.writer();
29192958 if (is_array) {
2920 try writer.writeAll(";\n");
29212959 try writer.writeAll("memcpy(");
29222960 try f.writeCValue(writer, local, .FunctionArgument);
29232961 try writer.writeAll(", ");
2924 } else try writer.writeAll(" = ");
2962 } else {
2963 try f.writeCValue(writer, local, .Other);
2964 try writer.writeAll(" = ");
2965 }
29252966 try f.writeCValue(writer, ptr, .Other);
29262967 try writer.writeByte('[');
29272968 try f.writeCValue(writer, index, .Other);
......@@ -2936,19 +2977,28 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
29362977}
29372978
29382979fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
2939 if (f.liveness.isUnused(inst)) return CValue.none;
2940
29412980 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
29422981 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
2982
2983 if (f.liveness.isUnused(inst)) {
2984 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
2985 return CValue.none;
2986 }
2987
29432988 const ptr_ty = f.air.typeOf(bin_op.lhs);
29442989 const child_ty = ptr_ty.childType();
29452990
29462991 const ptr = try f.resolveInst(bin_op.lhs);
2947 if (!child_ty.hasRuntimeBitsIgnoreComptime()) return ptr;
2992 if (!child_ty.hasRuntimeBitsIgnoreComptime()) {
2993 if (f.liveness.operandDies(inst, 1)) try die(f, inst, bin_op.rhs);
2994 return ptr;
2995 }
29482996 const index = try f.resolveInst(bin_op.rhs);
2997 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
29492998
29502999 const writer = f.object.writer();
2951 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
3000 const local = try f.allocLocal(inst, f.air.typeOfIndex(inst));
3001 try f.writeCValue(writer, local, .Other);
29523002 try writer.writeAll(" = &(");
29533003 if (ptr_ty.ptrSize() == .One) {
29543004 // It's a pointer to an array, so we need to de-reference.
......@@ -2967,22 +3017,29 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
29673017 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
29683018 const slice_ty = f.air.typeOf(bin_op.lhs);
29693019 if ((!slice_ty.isVolatilePtr() and f.liveness.isUnused(inst)) or
2970 !inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
3020 !inst_ty.hasRuntimeBitsIgnoreComptime())
3021 {
3022 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3023 return CValue.none;
3024 }
29713025
29723026 const slice = try f.resolveInst(bin_op.lhs);
29733027 const index = try f.resolveInst(bin_op.rhs);
3028 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
29743029
29753030 const target = f.object.dg.module.getTarget();
29763031 const is_array = lowersToArray(inst_ty, target);
29773032
2978 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
3033 const local = try f.allocLocal(inst, inst_ty);
29793034 const writer = f.object.writer();
29803035 if (is_array) {
2981 try writer.writeAll(";\n");
29823036 try writer.writeAll("memcpy(");
29833037 try f.writeCValue(writer, local, .FunctionArgument);
29843038 try writer.writeAll(", ");
2985 } else try writer.writeAll(" = ");
3039 } else {
3040 try f.writeCValue(writer, local, .Other);
3041 try writer.writeAll(" = ");
3042 }
29863043 try f.writeCValue(writer, slice, .Other);
29873044 try writer.writeAll(".ptr[");
29883045 try f.writeCValue(writer, index, .Other);
......@@ -2997,23 +3054,28 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
29973054}
29983055
29993056fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3000 if (f.liveness.isUnused(inst)) return CValue.none;
3001
30023057 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
30033058 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
30043059
3060 if (f.liveness.isUnused(inst)) {
3061 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3062 return CValue.none;
3063 }
3064
30053065 const slice_ty = f.air.typeOf(bin_op.lhs);
30063066 const child_ty = slice_ty.elemType2();
30073067 const slice = try f.resolveInst(bin_op.lhs);
3068 const index = try f.resolveInst(bin_op.rhs);
3069 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
30083070
30093071 const writer = f.object.writer();
3010 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
3072 const local = try f.allocLocal(inst, f.air.typeOfIndex(inst));
3073 try f.writeCValue(writer, local, .Other);
30113074 try writer.writeAll(" = ");
30123075 if (child_ty.hasRuntimeBitsIgnoreComptime()) try writer.writeByte('&');
30133076 try f.writeCValue(writer, slice, .Other);
30143077 try writer.writeAll(".ptr");
30153078 if (child_ty.hasRuntimeBitsIgnoreComptime()) {
3016 const index = try f.resolveInst(bin_op.rhs);
30173079 try writer.writeByte('[');
30183080 try f.writeCValue(writer, index, .Other);
30193081 try writer.writeByte(']');
......@@ -3023,24 +3085,30 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
30233085}
30243086
30253087fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3088 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
30263089 const inst_ty = f.air.typeOfIndex(inst);
3027 if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
3090 if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) {
3091 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3092 return CValue.none;
3093 }
30283094
3029 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
30303095 const array = try f.resolveInst(bin_op.lhs);
30313096 const index = try f.resolveInst(bin_op.rhs);
3097 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
30323098
30333099 const target = f.object.dg.module.getTarget();
30343100 const is_array = lowersToArray(inst_ty, target);
30353101
3036 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
3102 const local = try f.allocLocal(inst, inst_ty);
30373103 const writer = f.object.writer();
30383104 if (is_array) {
3039 try writer.writeAll(";\n");
30403105 try writer.writeAll("memcpy(");
30413106 try f.writeCValue(writer, local, .FunctionArgument);
30423107 try writer.writeAll(", ");
3043 } else try writer.writeAll(" = ");
3108 } else {
3109 try f.writeCValue(writer, local, .Other);
3110 try writer.writeAll(" = ");
3111 }
30443112 try f.writeCValue(writer, array, .Other);
30453113 try writer.writeByte('[');
30463114 try f.writeCValue(writer, index, .Other);
......@@ -3055,36 +3123,36 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
30553123}
30563124
30573125fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
3058 const writer = f.object.writer();
30593126 const inst_ty = f.air.typeOfIndex(inst);
30603127
30613128 const elem_type = inst_ty.elemType();
3062 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
30633129 if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) {
30643130 return CValue{ .undef = inst_ty };
30653131 }
30663132
3133 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
30673134 const target = f.object.dg.module.getTarget();
3068 // First line: the variable used as data storage.
30693135 const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target));
3070 try writer.writeAll(";\n");
3071
3136 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local });
3137 const gpa = f.object.dg.module.gpa;
3138 try f.allocs.put(gpa, local.local, false);
30723139 return CValue{ .local_ref = local.local };
30733140}
30743141
30753142fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3076 const writer = f.object.writer();
30773143 const inst_ty = f.air.typeOfIndex(inst);
30783144
30793145 const elem_ty = inst_ty.elemType();
3080 if (!elem_ty.hasRuntimeBitsIgnoreComptime()) {
3146 if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) {
30813147 return CValue{ .undef = inst_ty };
30823148 }
30833149
3084 // First line: the variable used as data storage.
3085 const local = try f.allocLocal(elem_ty, .Mut);
3086 try writer.writeAll(";\n");
3087
3150 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
3151 const target = f.object.dg.module.getTarget();
3152 const local = try f.allocAlignedLocal(elem_ty, mutability, inst_ty.ptrAlignment(target));
3153 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local });
3154 const gpa = f.object.dg.module.gpa;
3155 try f.allocs.put(gpa, local.local, false);
30883156 return CValue{ .local_ref = local.local };
30893157}
30903158
......@@ -3100,21 +3168,25 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
31003168 const src_ty = ptr_info.pointee_type;
31013169
31023170 if (!src_ty.hasRuntimeBitsIgnoreComptime() or
3103 !ptr_info.@"volatile" and f.liveness.isUnused(inst))
3171 (!ptr_info.@"volatile" and f.liveness.isUnused(inst)))
3172 {
3173 try reap(f, inst, &.{ty_op.operand});
31043174 return CValue.none;
3175 }
3176
3177 const operand = try f.resolveInst(ty_op.operand);
3178
3179 try reap(f, inst, &.{ty_op.operand});
31053180
31063181 const target = f.object.dg.module.getTarget();
31073182 const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target);
31083183 const is_array = lowersToArray(src_ty, target);
31093184 const need_memcpy = !is_aligned or is_array;
3110 const operand = try f.resolveInst(ty_op.operand);
31113185 const writer = f.object.writer();
31123186
3113 // We need to initialize arrays and unaligned loads with a memcpy so they must be mutable.
3114 const local = try f.allocLocal(src_ty, if (need_memcpy) .Mut else .Const);
3187 const local = try f.allocLocal(inst, src_ty);
31153188
31163189 if (need_memcpy) {
3117 try writer.writeAll(";\n");
31183190 try writer.writeAll("memcpy(");
31193191 if (!is_array) try writer.writeByte('&');
31203192 try f.writeCValue(writer, local, .FunctionArgument);
......@@ -3148,6 +3220,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
31483220 };
31493221 const field_ty = Type.initPayload(&field_pl.base);
31503222
3223 try f.writeCValue(writer, local, .Other);
31513224 try writer.writeAll(" = (");
31523225 try f.renderTypecast(writer, src_ty);
31533226 try writer.writeAll(")zig_wrap_");
......@@ -3162,6 +3235,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
31623235 try f.object.dg.renderBuiltinInfo(writer, field_ty, .Bits);
31633236 try writer.writeByte(')');
31643237 } else {
3238 try f.writeCValue(writer, local, .Other);
31653239 try writer.writeAll(" = ");
31663240 try f.writeCValueDeref(writer, operand);
31673241 }
......@@ -3181,9 +3255,10 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
31813255 if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) {
31823256 var deref = is_ptr;
31833257 const operand = try f.resolveInst(un_op);
3184 const ret_val = if (lowersToArray(ret_ty, target)) ret_val: {
3185 const array_local = try f.allocLocal(lowered_ret_ty, .Mut);
3186 try writer.writeAll(";\n");
3258 try reap(f, inst, &.{un_op});
3259 const is_array = lowersToArray(ret_ty, target);
3260 const ret_val = if (is_array) ret_val: {
3261 const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator()));
31873262 try writer.writeAll("memcpy(");
31883263 try f.writeCValueMember(writer, array_local, .{ .field = 0 });
31893264 try writer.writeAll(", ");
......@@ -3204,23 +3279,33 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
32043279 else
32053280 try f.writeCValue(writer, ret_val, .Other);
32063281 try writer.writeAll(";\n");
3207 } else if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) {
3208 // Not even allowed to return void in a naked function.
3209 try writer.writeAll("return;\n");
3282 if (is_array) {
3283 try freeLocal(f, inst, ret_val.local, 0);
3284 }
3285 } else {
3286 try reap(f, inst, &.{un_op});
3287 if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) {
3288 // Not even allowed to return void in a naked function.
3289 try writer.writeAll("return;\n");
3290 }
32103291 }
32113292 return CValue.none;
32123293}
32133294
32143295fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
3215 if (f.liveness.isUnused(inst))
3296 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3297
3298 if (f.liveness.isUnused(inst)) {
3299 try reap(f, inst, &.{ty_op.operand});
32163300 return CValue.none;
3301 }
32173302
3218 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
32193303 const operand = try f.resolveInst(ty_op.operand);
3220
3304 try reap(f, inst, &.{ty_op.operand});
32213305 const writer = f.object.writer();
32223306 const inst_ty = f.air.typeOfIndex(inst);
3223 const local = try f.allocLocal(inst_ty, .Const);
3307 const local = try f.allocLocal(inst, inst_ty);
3308 try f.writeCValue(writer, local, .Other);
32243309 try writer.writeAll(" = (");
32253310 try f.renderTypecast(writer, inst_ty);
32263311 try writer.writeByte(')');
......@@ -3230,17 +3315,22 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
32303315}
32313316
32323317fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3233 if (f.liveness.isUnused(inst)) return CValue.none;
3318 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3319 if (f.liveness.isUnused(inst)) {
3320 try reap(f, inst, &.{ty_op.operand});
3321 return CValue.none;
3322 }
32343323
3324 const operand = try f.resolveInst(ty_op.operand);
3325 try reap(f, inst, &.{ty_op.operand});
32353326 const inst_ty = f.air.typeOfIndex(inst);
3236 const local = try f.allocLocal(inst_ty, .Const);
3237 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
32383327 const writer = f.object.writer();
3239 const operand = try f.resolveInst(ty_op.operand);
3328 const local = try f.allocLocal(inst, inst_ty);
32403329 const target = f.object.dg.module.getTarget();
32413330 const dest_int_info = inst_ty.intInfo(target);
32423331 const dest_bits = dest_int_info.bits;
32433332
3333 try f.writeCValue(writer, local, .Other);
32443334 try writer.writeAll(" = (");
32453335 try f.renderTypecast(writer, inst_ty);
32463336 try writer.writeByte(')');
......@@ -3282,20 +3372,24 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
32823372}
32833373
32843374fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
3285 if (f.liveness.isUnused(inst))
3286 return CValue.none;
32873375 const un_op = f.air.instructions.items(.data)[inst].un_op;
3376 if (f.liveness.isUnused(inst)) {
3377 try reap(f, inst, &.{un_op});
3378 return CValue.none;
3379 }
3380 const operand = try f.resolveInst(un_op);
3381 try reap(f, inst, &.{un_op});
32883382 const writer = f.object.writer();
32893383 const inst_ty = f.air.typeOfIndex(inst);
3290 const operand = try f.resolveInst(un_op);
3291 const local = try f.allocLocal(inst_ty, .Const);
3384 const local = try f.allocLocal(inst, inst_ty);
3385 try f.writeCValue(writer, local, .Other);
32923386 try writer.writeAll(" = ");
32933387 try f.writeCValue(writer, operand, .Other);
32943388 try writer.writeAll(";\n");
32953389 return local;
32963390}
32973391
3298fn airStoreUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {
3392fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {
32993393 if (f.wantSafety()) {
33003394 const writer = f.object.writer();
33013395 try writer.writeAll("memset(");
......@@ -3311,18 +3405,23 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
33113405 // *a = b;
33123406 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
33133407 const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data;
3314 if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) return CValue.none;
3408 if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) {
3409 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3410 return CValue.none;
3411 }
33153412
33163413 const ptr_val = try f.resolveInst(bin_op.lhs);
33173414 const src_ty = f.air.typeOf(bin_op.rhs);
33183415 const src_val = try f.resolveInst(bin_op.rhs);
33193416
3417 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3418
33203419 // TODO Sema should emit a different instruction when the store should
33213420 // possibly do the safety 0xaa bytes for undefined.
33223421 const src_val_is_undefined =
33233422 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
33243423 if (src_val_is_undefined)
3325 return try airStoreUndefined(f, ptr_info.pointee_type, ptr_val);
3424 return try storeUndefined(f, ptr_info.pointee_type, ptr_val);
33263425
33273426 const target = f.object.dg.module.getTarget();
33283427 const is_aligned = ptr_info.@"align" == 0 or
......@@ -3340,7 +3439,8 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
33403439 // so work around this by initializing into new local.
33413440 // TODO this should be done by manually initializing elements of the dest array
33423441 const array_src = if (src_val == .constant) blk: {
3343 const new_local = try f.allocLocal(src_ty, .Const);
3442 const new_local = try f.allocLocal(inst, src_ty);
3443 try f.writeCValue(writer, new_local, .Other);
33443444 try writer.writeAll(" = ");
33453445 try f.writeCValue(writer, src_val, .Initializer);
33463446 try writer.writeAll(";\n");
......@@ -3356,6 +3456,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
33563456 try writer.writeAll(", sizeof(");
33573457 try f.renderTypecast(writer, src_ty);
33583458 try writer.writeAll("))");
3459 if (src_val == .constant) {
3460 try freeLocal(f, inst, array_src.local, 0);
3461 }
33593462 } else if (ptr_info.host_size != 0) {
33603463 const host_bits = ptr_info.host_size * 8;
33613464 var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits };
......@@ -3421,22 +3524,24 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
34213524}
34223525
34233526fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue {
3424 if (f.liveness.isUnused(inst))
3425 return CValue.none;
3426
34273527 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
34283528 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
34293529
3530 if (f.liveness.isUnused(inst)) {
3531 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3532 return CValue.none;
3533 }
3534
34303535 const lhs = try f.resolveInst(bin_op.lhs);
34313536 const rhs = try f.resolveInst(bin_op.rhs);
3537 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
34323538
34333539 const inst_ty = f.air.typeOfIndex(inst);
34343540 const vector_ty = f.air.typeOf(bin_op.lhs);
34353541 const scalar_ty = vector_ty.scalarType();
34363542 const w = f.object.writer();
34373543
3438 const local = try f.allocLocal(inst_ty, .Mut);
3439 try w.writeAll(";\n");
3544 const local = try f.allocLocal(inst, inst_ty);
34403545
34413546 switch (vector_ty.zigTypeTag()) {
34423547 .Vector => {
......@@ -3471,21 +3576,27 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
34713576 return local;
34723577}
34733578
3474fn airNot(f: *Function, inst: Air.Inst.Index) !void {
3579fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
34753580 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3581
3582 if (f.liveness.isUnused(inst)) {
3583 try reap(f, inst, &.{ty_op.operand});
3584 return CValue.none;
3585 }
3586
34763587 const op = try f.resolveInst(ty_op.operand);
3588 try reap(f, inst, &.{ty_op.operand});
34773589
34783590 const writer = f.object.writer();
34793591 const inst_ty = f.air.typeOfIndex(inst);
3480
3481 const target = f.object.dg.module.getTarget();
3482 if (inst_ty.bitSize(target) > 64) {}
3483
3484 try writer.writeByte('(');
3485 try f.renderTypecast(writer, inst_ty);
3486 try writer.writeByte(')');
3592 const local = try f.allocLocal(inst, inst_ty);
3593 try f.writeCValue(writer, local, .Other);
3594 try writer.writeAll(" = ");
34873595 try writer.writeByte(if (inst_ty.tag() == .bool) '!' else '~');
34883596 try f.writeCValue(writer, op, .Other);
3597 try writer.writeAll(";\n");
3598
3599 return local;
34893600}
34903601
34913602fn airBinOp(
......@@ -3494,54 +3605,68 @@ fn airBinOp(
34943605 operator: []const u8,
34953606 operation: []const u8,
34963607 info: BuiltinInfo,
3497) !void {
3608) !CValue {
34983609 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3499
35003610 const operand_ty = f.air.typeOf(bin_op.lhs);
35013611 const target = f.object.dg.module.getTarget();
35023612 if ((operand_ty.isInt() and operand_ty.bitSize(target) > 64) or operand_ty.isRuntimeFloat())
3503 return airBinBuiltinCall(f, inst, operation, info);
3613 return try airBinBuiltinCall(f, inst, operation, info);
35043614
3505 const inst_ty = f.air.typeOfIndex(inst);
35063615 const lhs = try f.resolveInst(bin_op.lhs);
35073616 const rhs = try f.resolveInst(bin_op.rhs);
35083617
3618 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3619
3620 if (f.liveness.isUnused(inst)) return CValue.none;
3621
3622 const inst_ty = f.air.typeOfIndex(inst);
3623
35093624 const writer = f.object.writer();
3510 try writer.writeByte('(');
3511 try f.renderTypecast(writer, inst_ty);
3512 try writer.writeAll(")(");
3625 const local = try f.allocLocal(inst, inst_ty);
3626 try f.writeCValue(writer, local, .Other);
3627 try writer.writeAll(" = ");
35133628 try f.writeCValue(writer, lhs, .Other);
35143629 try writer.writeByte(' ');
35153630 try writer.writeAll(operator);
35163631 try writer.writeByte(' ');
35173632 try f.writeCValue(writer, rhs, .Other);
3518 try writer.writeByte(')');
3633 try writer.writeAll(";\n");
3634
3635 return local;
35193636}
35203637
3521fn airCmpOp(
3522 f: *Function,
3523 inst: Air.Inst.Index,
3524 operator: []const u8,
3525 operation: []const u8,
3526) !void {
3638fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: []const u8) !CValue {
35273639 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
35283640
3641 if (f.liveness.isUnused(inst)) {
3642 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3643 return CValue.none;
3644 }
3645
35293646 const operand_ty = f.air.typeOf(bin_op.lhs);
35303647 const target = f.object.dg.module.getTarget();
35313648 if (operand_ty.isInt() and operand_ty.bitSize(target) > 64)
3532 return airCmpBuiltinCall(f, inst, operator, "cmp");
3649 return try cmpBuiltinCall(f, inst, operator, "cmp");
35333650 if (operand_ty.isRuntimeFloat())
3534 return airCmpBuiltinCall(f, inst, operator, operation);
3651 return try cmpBuiltinCall(f, inst, operator, operation);
35353652
3653 const inst_ty = f.air.typeOfIndex(inst);
35363654 const lhs = try f.resolveInst(bin_op.lhs);
35373655 const rhs = try f.resolveInst(bin_op.rhs);
3656 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
35383657
35393658 const writer = f.object.writer();
3659 const local = try f.allocLocal(inst, inst_ty);
3660 try f.writeCValue(writer, local, .Other);
3661 try writer.writeAll(" = ");
35403662 try f.writeCValue(writer, lhs, .Other);
35413663 try writer.writeByte(' ');
35423664 try writer.writeAll(operator);
35433665 try writer.writeByte(' ');
35443666 try f.writeCValue(writer, rhs, .Other);
3667 try writer.writeAll(";\n");
3668
3669 return local;
35453670}
35463671
35473672fn airEquality(
......@@ -3550,20 +3675,31 @@ fn airEquality(
35503675 negate_prefix: []const u8,
35513676 operator: []const u8,
35523677 operation: []const u8,
3553) !void {
3678) !CValue {
35543679 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
35553680
3681 if (f.liveness.isUnused(inst)) {
3682 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3683 return CValue.none;
3684 }
3685
35563686 const operand_ty = f.air.typeOf(bin_op.lhs);
35573687 const target = f.object.dg.module.getTarget();
35583688 if (operand_ty.isInt() and operand_ty.bitSize(target) > 64)
3559 return airCmpBuiltinCall(f, inst, operator, "cmp");
3689 return try cmpBuiltinCall(f, inst, operator, "cmp");
35603690 if (operand_ty.isRuntimeFloat())
3561 return airCmpBuiltinCall(f, inst, operator, operation);
3691 return try cmpBuiltinCall(f, inst, operator, operation);
35623692
35633693 const lhs = try f.resolveInst(bin_op.lhs);
35643694 const rhs = try f.resolveInst(bin_op.rhs);
3695 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
35653696
35663697 const writer = f.object.writer();
3698 const inst_ty = f.air.typeOfIndex(inst);
3699 const local = try f.allocLocal(inst, inst_ty);
3700 try f.writeCValue(writer, local, .Other);
3701 try writer.writeAll(" = ");
3702
35673703 if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) {
35683704 // (A && B) || (C && (A == B))
35693705 // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload
......@@ -3580,8 +3716,9 @@ fn airEquality(
35803716 try f.writeCValue(writer, lhs, .Other);
35813717 try writer.writeAll(".is_null == ");
35823718 try f.writeCValue(writer, rhs, .Other);
3583 try writer.writeAll(".is_null))");
3584 return;
3719 try writer.writeAll(".is_null));\n");
3720
3721 return local;
35853722 }
35863723
35873724 try f.writeCValue(writer, lhs, .Other);
......@@ -3589,17 +3726,26 @@ fn airEquality(
35893726 try writer.writeAll(operator);
35903727 try writer.writeByte(' ');
35913728 try f.writeCValue(writer, rhs, .Other);
3729 try writer.writeAll(";\n");
3730
3731 return local;
35923732}
35933733
35943734fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
3595 if (f.liveness.isUnused(inst)) return CValue.none;
3596
35973735 const un_op = f.air.instructions.items(.data)[inst].un_op;
3736
3737 if (f.liveness.isUnused(inst)) {
3738 try reap(f, inst, &.{un_op});
3739 return CValue.none;
3740 }
3741
35983742 const inst_ty = f.air.typeOfIndex(inst);
35993743 const operand = try f.resolveInst(un_op);
3744 try reap(f, inst, &.{un_op});
36003745
36013746 const writer = f.object.writer();
3602 const local = try f.allocLocal(inst_ty, .Const);
3747 const local = try f.allocLocal(inst, inst_ty);
3748 try f.writeCValue(writer, local, .Other);
36033749 try writer.writeAll(" = ");
36043750 try f.writeCValue(writer, operand, .Other);
36053751 try writer.print(" < sizeof({ }) / sizeof(*{0 });\n", .{fmtIdent("zig_errorName")});
......@@ -3607,16 +3753,18 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
36073753}
36083754
36093755fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
3610 if (f.liveness.isUnused(inst)) return CValue.none;
3611
36123756 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
36133757 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
3758 if (f.liveness.isUnused(inst)) {
3759 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3760 return CValue.none;
3761 }
3762
36143763 const lhs = try f.resolveInst(bin_op.lhs);
36153764 const rhs = try f.resolveInst(bin_op.rhs);
3765 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36163766
3617 const writer = f.object.writer();
36183767 const inst_ty = f.air.typeOfIndex(inst);
3619 const local = try f.allocLocal(inst_ty, .Const);
36203768 const elem_ty = switch (inst_ty.ptrSize()) {
36213769 .One => blk: {
36223770 const array_ty = inst_ty.childType();
......@@ -3625,8 +3773,12 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
36253773 else => inst_ty.childType(),
36263774 };
36273775
3628 // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer,
3629 // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced.
3776 // We must convert to and from integer types to prevent UB if the operation
3777 // results in a NULL pointer, or if LHS is NULL. The operation is only UB
3778 // if the result is NULL and then dereferenced.
3779 const local = try f.allocLocal(inst, inst_ty);
3780 const writer = f.object.writer();
3781 try f.writeCValue(writer, local, .Other);
36303782 try writer.writeAll(" = (");
36313783 try f.renderTypecast(writer, inst_ty);
36323784 try writer.writeAll(")(((uintptr_t)");
......@@ -3642,23 +3794,30 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
36423794 return local;
36433795}
36443796
3645fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !void {
3797fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue {
36463798 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
36473799
3800 if (f.liveness.isUnused(inst)) {
3801 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3802 return CValue.none;
3803 }
3804
36483805 const inst_ty = f.air.typeOfIndex(inst);
36493806 const target = f.object.dg.module.getTarget();
36503807 if (inst_ty.isInt() and inst_ty.bitSize(target) > 64)
3651 return airBinBuiltinCall(f, inst, operation[1..], .None);
3808 return try airBinBuiltinCall(f, inst, operation[1..], .None);
36523809 if (inst_ty.isRuntimeFloat())
3653 return airBinFloatOp(f, inst, operation);
3810 return try airBinFloatOp(f, inst, operation);
36543811
36553812 const lhs = try f.resolveInst(bin_op.lhs);
36563813 const rhs = try f.resolveInst(bin_op.rhs);
3814 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36573815
36583816 const writer = f.object.writer();
3659
3817 const local = try f.allocLocal(inst, inst_ty);
3818 try f.writeCValue(writer, local, .Other);
36603819 // (lhs <> rhs) ? lhs : rhs
3661 try writer.writeAll("(");
3820 try writer.writeAll(" = (");
36623821 try f.writeCValue(writer, lhs, .Other);
36633822 try writer.writeByte(' ');
36643823 try writer.writeByte(operator);
......@@ -3668,28 +3827,38 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
36683827 try f.writeCValue(writer, lhs, .Other);
36693828 try writer.writeAll(" : ");
36703829 try f.writeCValue(writer, rhs, .Other);
3830 try writer.writeAll(";\n");
3831
3832 return local;
36713833}
36723834
36733835fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
3674 if (f.liveness.isUnused(inst)) return CValue.none;
3675
36763836 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
36773837 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
3838
3839 if (f.liveness.isUnused(inst)) {
3840 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3841 return CValue.none;
3842 }
3843
36783844 const ptr = try f.resolveInst(bin_op.lhs);
36793845 const len = try f.resolveInst(bin_op.rhs);
3846 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36803847
36813848 const writer = f.object.writer();
36823849 const inst_ty = f.air.typeOfIndex(inst);
3683 const local = try f.allocLocal(inst_ty, .Const);
3684
3685 try writer.writeAll(" = {(");
3850 const local = try f.allocLocal(inst, inst_ty);
3851 try f.writeCValue(writer, local, .Other);
3852 try writer.writeAll(".ptr = (");
36863853 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
36873854 try f.renderTypecast(writer, inst_ty.slicePtrFieldType(&buf));
36883855 try writer.writeByte(')');
36893856 try f.writeCValue(writer, ptr, .Other);
3690 try writer.writeAll(", ");
3857 try writer.writeAll("; ");
3858 try f.writeCValue(writer, local, .Other);
3859 try writer.writeAll(".len = ");
36913860 try f.writeCValue(writer, len, .Initializer);
3692 try writer.writeAll("};\n");
3861 try writer.writeAll(";\n");
36933862
36943863 return local;
36953864}
......@@ -3701,6 +3870,7 @@ fn airCall(
37013870) !CValue {
37023871 // Not even allowed to call panic in a naked function.
37033872 if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none;
3873 const gpa = f.object.dg.gpa;
37043874
37053875 switch (modifier) {
37063876 .auto => {},
......@@ -3712,6 +3882,21 @@ fn airCall(
37123882 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
37133883 const extra = f.air.extraData(Air.Call, pl_op.payload);
37143884 const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]);
3885
3886 const resolved_args = try gpa.alloc(CValue, args.len);
3887 defer gpa.free(resolved_args);
3888 for (args) |arg, i| {
3889 resolved_args[i] = try f.resolveInst(arg);
3890 }
3891
3892 const callee = try f.resolveInst(pl_op.operand);
3893
3894 {
3895 var bt = iterateBigTomb(f, inst);
3896 try bt.feed(pl_op.operand);
3897 for (args) |arg| try bt.feed(arg);
3898 }
3899
37153900 const callee_ty = f.air.typeOf(pl_op.operand);
37163901 const fn_ty = switch (callee_ty.zigTypeTag()) {
37173902 .Fn => callee_ty,
......@@ -3733,7 +3918,8 @@ fn airCall(
37333918 try writer.writeByte(')');
37343919 break :r .none;
37353920 } else r: {
3736 const local = try f.allocLocal(lowered_ret_ty, .Const);
3921 const local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator()));
3922 try f.writeCValue(writer, local, .Other);
37373923 try writer.writeAll(" = ");
37383924 break :r local;
37393925 };
......@@ -3759,13 +3945,12 @@ fn airCall(
37593945 break :callee;
37603946 }
37613947 // Fall back to function pointer call.
3762 const callee = try f.resolveInst(pl_op.operand);
37633948 try f.writeCValue(writer, callee, .Other);
37643949 }
37653950
37663951 try writer.writeByte('(');
37673952 var args_written: usize = 0;
3768 for (args) |arg| {
3953 for (args) |arg, arg_i| {
37693954 const ty = f.air.typeOf(arg);
37703955 if (!ty.hasRuntimeBitsIgnoreComptime()) continue;
37713956 if (args_written != 0) {
......@@ -3780,23 +3965,28 @@ fn airCall(
37803965 if (ty.isVolatilePtr()) try writer.writeAll(" volatile");
37813966 try writer.writeAll(" *)");
37823967 }
3783 try f.writeCValue(writer, try f.resolveInst(arg), .FunctionArgument);
3968 try f.writeCValue(writer, resolved_args[arg_i], .FunctionArgument);
37843969 args_written += 1;
37853970 }
37863971 try writer.writeAll(");\n");
37873972
3788 if (result_local == .none or !lowersToArray(ret_ty, target)) return result_local;
3973 const result = r: {
3974 if (result_local == .none or !lowersToArray(ret_ty, target))
3975 break :r result_local;
37893976
3790 const array_local = try f.allocLocal(ret_ty, .Mut);
3791 try writer.writeAll(";\n");
3792 try writer.writeAll("memcpy(");
3793 try f.writeCValue(writer, array_local, .FunctionArgument);
3794 try writer.writeAll(", ");
3795 try f.writeCValueMember(writer, result_local, .{ .field = 0 });
3796 try writer.writeAll(", sizeof(");
3797 try f.renderTypecast(writer, ret_ty);
3798 try writer.writeAll("));\n");
3799 return array_local;
3977 const array_local = try f.allocLocal(inst, ret_ty);
3978 try writer.writeAll("memcpy(");
3979 try f.writeCValue(writer, array_local, .FunctionArgument);
3980 try writer.writeAll(", ");
3981 try f.writeCValueMember(writer, result_local, .{ .field = 0 });
3982 try writer.writeAll(", sizeof(");
3983 try f.renderTypecast(writer, ret_ty);
3984 try writer.writeAll("));\n");
3985 try freeLocal(f, inst, result_local.local, 0);
3986 break :r array_local;
3987 };
3988
3989 return result;
38003990}
38013991
38023992fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
......@@ -3828,6 +4018,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
38284018 const name = f.air.nullTerminatedString(pl_op.payload);
38294019 const operand = try f.resolveInst(pl_op.operand);
38304020 _ = operand;
4021 try reap(f, inst, &.{pl_op.operand});
38314022 const writer = f.object.writer();
38324023 try writer.print("/* var:{s} */\n", .{name});
38334024 return CValue.none;
......@@ -3843,12 +4034,10 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
38434034 const writer = f.object.writer();
38444035
38454036 const inst_ty = f.air.typeOfIndex(inst);
3846 const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) blk: {
3847 // allocate a location for the result
3848 const local = try f.allocLocal(inst_ty, .Mut);
3849 try writer.writeAll(";\n");
3850 break :blk local;
3851 } else CValue{ .none = {} };
4037 const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst))
4038 try f.allocLocal(inst, inst_ty)
4039 else
4040 CValue{ .none = {} };
38524041
38534042 try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{
38544043 .block_id = block_id,
......@@ -3864,32 +4053,30 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
38644053
38654054fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {
38664055 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
3867 const err_union = try f.resolveInst(pl_op.operand);
38684056 const extra = f.air.extraData(Air.Try, pl_op.payload);
38694057 const body = f.air.extra[extra.end..][0..extra.data.body_len];
38704058 const err_union_ty = f.air.typeOf(pl_op.operand);
3871 const result_ty = f.air.typeOfIndex(inst);
3872 return lowerTry(f, err_union, body, err_union_ty, false, result_ty);
4059 return lowerTry(f, inst, pl_op.operand, body, err_union_ty, false);
38734060}
38744061
38754062fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue {
38764063 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
38774064 const extra = f.air.extraData(Air.TryPtr, ty_pl.payload);
3878 const err_union_ptr = try f.resolveInst(extra.data.ptr);
38794065 const body = f.air.extra[extra.end..][0..extra.data.body_len];
38804066 const err_union_ty = f.air.typeOf(extra.data.ptr).childType();
3881 const result_ty = f.air.typeOfIndex(inst);
3882 return lowerTry(f, err_union_ptr, body, err_union_ty, true, result_ty);
4067 return lowerTry(f, inst, extra.data.ptr, body, err_union_ty, true);
38834068}
38844069
38854070fn lowerTry(
38864071 f: *Function,
3887 err_union: CValue,
4072 inst: Air.Inst.Index,
4073 operand: Air.Inst.Ref,
38884074 body: []const Air.Inst.Index,
38894075 err_union_ty: Type,
38904076 operand_is_ptr: bool,
3891 result_ty: Type,
38924077) !CValue {
4078 const err_union = try f.resolveInst(operand);
4079 const result_ty = f.air.typeOfIndex(inst);
38934080 const writer = f.object.writer();
38944081 const payload_ty = err_union_ty.errorUnionPayload();
38954082 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
......@@ -3902,6 +4089,10 @@ fn lowerTry(
39024089 else
39034090 try f.writeCValue(writer, err_union, .Other);
39044091 } else {
4092 // Reap the operand so that it can be reused inside genBody.
4093 // Remember we must avoid calling reap() twice for the same operand
4094 // in this function.
4095 try reap(f, inst, &.{operand});
39054096 if (operand_is_ptr or isByRef(err_union_ty))
39064097 try f.writeCValueDerefMember(writer, err_union, .{ .identifier = "error" })
39074098 else
......@@ -3921,11 +4112,16 @@ fn lowerTry(
39214112 }
39224113 }
39234114
4115 try reap(f, inst, &.{operand});
4116
4117 if (f.liveness.isUnused(inst)) {
4118 return CValue.none;
4119 }
4120
39244121 const target = f.object.dg.module.getTarget();
39254122 const is_array = lowersToArray(payload_ty, target);
3926 const local = try f.allocLocal(result_ty, if (is_array) .Mut else .Const);
4123 const local = try f.allocLocal(inst, result_ty);
39274124 if (is_array) {
3928 try writer.writeAll(";\n");
39294125 try writer.writeAll("memcpy(");
39304126 try f.writeCValue(writer, local, .FunctionArgument);
39314127 try writer.writeAll(", ");
......@@ -3934,6 +4130,7 @@ fn lowerTry(
39344130 try f.renderTypecast(writer, payload_ty);
39354131 try writer.writeAll("));\n");
39364132 } else {
4133 try f.writeCValue(writer, local, .Other);
39374134 try writer.writeAll(" = ");
39384135 if (operand_is_ptr or isByRef(payload_ty)) {
39394136 try writer.writeByte('&');
......@@ -3953,6 +4150,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
39534150 // If result is .none then the value of the block is unused.
39544151 if (result != .none) {
39554152 const operand = try f.resolveInst(branch.operand);
4153 try reap(f, inst, &.{branch.operand});
39564154
39574155 const operand_ty = f.air.typeOf(branch.operand);
39584156 const target = f.object.dg.module.getTarget();
......@@ -3977,40 +4175,50 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
39774175}
39784176
39794177fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
3980 const src_ty = f.air.typeOfIndex(inst);
4178 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
4179 const dest_ty = f.air.typeOfIndex(inst);
39814180 // No IgnoreComptime until Sema stops giving us garbage Air.
39824181 // https://github.com/ziglang/zig/issues/13410
3983 if (f.liveness.isUnused(inst) or !src_ty.hasRuntimeBits()) return CValue.none;
4182 if (f.liveness.isUnused(inst) or !dest_ty.hasRuntimeBits()) {
4183 try reap(f, inst, &.{ty_op.operand});
4184 return CValue.none;
4185 }
39844186
3985 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3986 const operand = try f.resolveInstNoInline(ty_op.operand);
3987 const dest_ty = f.air.typeOf(ty_op.operand);
4187 const operand = try f.resolveInst(ty_op.operand);
4188 try reap(f, inst, &.{ty_op.operand});
4189 const operand_ty = f.air.typeOf(ty_op.operand);
39884190 const target = f.object.dg.module.getTarget();
4191 const writer = f.object.writer();
4192
4193 const local = try f.allocLocal(inst, dest_ty);
39894194
3990 if (dest_ty.isAbiInt() and src_ty.isAbiInt()) {
3991 const src_info = src_ty.intInfo(target);
3992 const dest_info = dest_ty.intInfo(target);
3993 if (std.meta.eql(src_info, dest_info)) {
3994 return operand;
4195 if (operand_ty.isAbiInt() and dest_ty.isAbiInt()) {
4196 const src_info = dest_ty.intInfo(target);
4197 const dest_info = operand_ty.intInfo(target);
4198 if (src_info.signedness == dest_info.signedness and
4199 src_info.bits == dest_info.bits)
4200 {
4201 try f.writeCValue(writer, local, .Other);
4202 try writer.writeAll(" = ");
4203 try f.writeCValue(writer, operand, .Other);
4204 try writer.writeAll(";\n");
4205 return local;
39954206 }
39964207 }
39974208
3998 const writer = f.object.writer();
3999 if (src_ty.isPtrAtRuntime() and dest_ty.isPtrAtRuntime()) {
4000 const local = try f.allocLocal(src_ty, .Const);
4209 if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) {
4210 try f.writeCValue(writer, local, .Other);
40014211 try writer.writeAll(" = (");
4002 try f.renderTypecast(writer, src_ty);
4212 try f.renderTypecast(writer, dest_ty);
40034213 try writer.writeByte(')');
40044214 try f.writeCValue(writer, operand, .Other);
40054215 try writer.writeAll(";\n");
40064216 return local;
40074217 }
40084218
4009 const local = try f.allocLocal(src_ty, .Mut);
4010 try writer.writeAll(";\n");
4011
40124219 const operand_lval = if (operand == .constant) blk: {
4013 const operand_local = try f.allocLocal(dest_ty, .Const);
4220 const operand_local = try f.allocLocal(inst, operand_ty);
4221 try f.writeCValue(writer, operand_local, .Other);
40144222 try writer.writeAll(" = ");
40154223 try f.writeCValue(writer, operand, .Initializer);
40164224 try writer.writeAll(";\n");
......@@ -4022,20 +4230,24 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
40224230 try writer.writeAll(", &");
40234231 try f.writeCValue(writer, operand_lval, .Other);
40244232 try writer.writeAll(", sizeof(");
4025 try f.renderTypecast(writer, src_ty);
4233 try f.renderTypecast(writer, dest_ty);
40264234 try writer.writeAll("));\n");
40274235
40284236 // Ensure padding bits have the expected value.
4029 if (src_ty.isAbiInt()) {
4237 if (dest_ty.isAbiInt()) {
40304238 try f.writeCValue(writer, local, .Other);
40314239 try writer.writeAll(" = zig_wrap_");
4032 try f.object.dg.renderTypeForBuiltinFnName(writer, src_ty);
4240 try f.object.dg.renderTypeForBuiltinFnName(writer, dest_ty);
40334241 try writer.writeByte('(');
40344242 try f.writeCValue(writer, local, .Other);
4035 try f.object.dg.renderBuiltinInfo(writer, src_ty, .Bits);
4243 try f.object.dg.renderBuiltinInfo(writer, dest_ty, .Bits);
40364244 try writer.writeAll(");\n");
40374245 }
40384246
4247 if (operand == .constant) {
4248 try freeLocal(f, inst, operand_lval.local, 0);
4249 }
4250
40394251 return local;
40404252}
40414253
......@@ -4047,7 +4259,8 @@ fn airBreakpoint(writer: anytype) !CValue {
40474259fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
40484260 if (f.liveness.isUnused(inst)) return CValue.none;
40494261 const writer = f.object.writer();
4050 const local = try f.allocLocal(Type.usize, .Const);
4262 const local = try f.allocLocal(inst, Type.usize);
4263 try f.writeCValue(writer, local, .Other);
40514264 try writer.writeAll(" = (");
40524265 try f.renderTypecast(writer, Type.usize);
40534266 try writer.writeAll(")zig_return_address();\n");
......@@ -4057,7 +4270,8 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
40574270fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
40584271 if (f.liveness.isUnused(inst)) return CValue.none;
40594272 const writer = f.object.writer();
4060 const local = try f.allocLocal(Type.usize, .Const);
4273 const local = try f.allocLocal(inst, Type.usize);
4274 try f.writeCValue(writer, local, .Other);
40614275 try writer.writeAll(" = (");
40624276 try f.renderTypecast(writer, Type.usize);
40634277 try writer.writeAll(")zig_frame_address();\n");
......@@ -4088,27 +4302,79 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue {
40884302 const loop = f.air.extraData(Air.Block, ty_pl.payload);
40894303 const body = f.air.extra[loop.end..][0..loop.data.body_len];
40904304 const writer = f.object.writer();
4091 try writer.writeAll("while (");
4092 try f.object.dg.renderValue(writer, Type.bool, Value.true, .condition);
4093 try writer.writeAll(") ");
4305
4306 const gpa = f.object.dg.gpa;
4307 try f.free_locals_stack.insert(gpa, f.free_locals_stack.items.len - 1, .{});
4308
4309 try writer.writeAll("for (;;) ");
40944310 try genBody(f, body);
40954311 try writer.writeByte('\n');
4312
4313 var old_free_locals = f.free_locals_stack.pop();
4314 defer deinitFreeLocalsMap(gpa, &old_free_locals);
4315 const new_free_locals = f.getFreeLocals();
4316 var it = new_free_locals.iterator();
4317 while (it.next()) |entry| {
4318 const gop = try old_free_locals.getOrPutContext(gpa, entry.key_ptr.*, f.tyHashCtx());
4319 if (gop.found_existing) {
4320 try gop.value_ptr.appendSlice(gpa, entry.value_ptr.items);
4321 } else {
4322 gop.value_ptr.* = entry.value_ptr.*;
4323 entry.value_ptr.* = .{};
4324 }
4325 }
4326 deinitFreeLocalsMap(gpa, new_free_locals);
4327 new_free_locals.* = old_free_locals.move();
4328
40964329 return CValue.none;
40974330}
40984331
40994332fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
41004333 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
41014334 const cond = try f.resolveInst(pl_op.operand);
4335 try reap(f, inst, &.{pl_op.operand});
41024336 const extra = f.air.extraData(Air.CondBr, pl_op.payload);
41034337 const then_body = f.air.extra[extra.end..][0..extra.data.then_body_len];
41044338 const else_body = f.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
4339 const liveness_condbr = f.liveness.getCondBr(inst);
41054340 const writer = f.object.writer();
41064341
4342 // Keep using the original for the then branch; use a clone of the value
4343 // map for the else branch.
4344 const gpa = f.object.dg.gpa;
4345 var cloned_map = try f.value_map.clone();
4346 defer cloned_map.deinit();
4347 var cloned_frees = try cloneFreeLocalsMap(gpa, f.getFreeLocals());
4348 defer deinitFreeLocalsMap(gpa, &cloned_frees);
4349
4350 // Remember how many locals there were before entering the then branch so
4351 // that we can notice and use them in the else branch. Any new locals must
4352 // necessarily be free already after the then branch is complete.
4353 const pre_locals_len = @intCast(LocalIndex, f.locals.items.len);
4354 const pre_clone_depth = f.free_locals_clone_depth;
4355 f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len);
4356
4357 for (liveness_condbr.then_deaths) |operand| {
4358 try die(f, inst, Air.indexToRef(operand));
4359 }
4360
41074361 try writer.writeAll("if (");
4108 try f.writeCValue(writer, cond, .condition);
4362 try f.writeCValue(writer, cond, .Other);
41094363 try writer.writeAll(") ");
41104364 try genBody(f, then_body);
41114365 try writer.writeAll(" else ");
4366 f.value_map.deinit();
4367 f.value_map = cloned_map.move();
4368 const free_locals = f.getFreeLocals();
4369 deinitFreeLocalsMap(gpa, free_locals);
4370 free_locals.* = cloned_frees.move();
4371 f.free_locals_clone_depth = pre_clone_depth;
4372 for (liveness_condbr.else_deaths) |operand| {
4373 try die(f, inst, Air.indexToRef(operand));
4374 }
4375
4376 try noticeBranchFrees(f, pre_locals_len, inst);
4377
41124378 try genBody(f, else_body);
41134379 try f.object.indent_writer.insertNewline();
41144380
......@@ -4118,6 +4384,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue {
41184384fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
41194385 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
41204386 const condition = try f.resolveInst(pl_op.operand);
4387 try reap(f, inst, &.{pl_op.operand});
41214388 const condition_ty = f.air.typeOf(pl_op.operand);
41224389 const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload);
41234390 const writer = f.object.writer();
......@@ -4136,6 +4403,15 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
41364403 try writer.writeAll(") {");
41374404 f.object.indent_writer.pushIndent();
41384405
4406 const gpa = f.object.dg.gpa;
4407 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.data.cases_len + 1);
4408 defer gpa.free(liveness.deaths);
4409
4410 // On the final iteration we do not clone the map. This ensures that
4411 // lowering proceeds after the switch_br taking into account the
4412 // mutations to the liveness information.
4413 const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0);
4414
41394415 var extra_index: usize = switch_br.end;
41404416 var case_i: u32 = 0;
41414417 while (case_i < switch_br.data.cases_len) : (case_i += 1) {
......@@ -4155,14 +4431,61 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
41554431 try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?, .Other);
41564432 try writer.writeAll(": ");
41574433 }
4434
4435 if (case_i != last_case_i) {
4436 const old_value_map = f.value_map;
4437 f.value_map = try old_value_map.clone();
4438 var free_locals = f.getFreeLocals();
4439 const old_free_locals = free_locals.*;
4440 free_locals.* = try cloneFreeLocalsMap(gpa, free_locals);
4441
4442 // Remember how many locals there were before entering each branch so that
4443 // we can notice and use them in subsequent branches. Any new locals must
4444 // necessarily be free already after the previous branch is complete.
4445 const pre_locals_len = @intCast(LocalIndex, f.locals.items.len);
4446 const pre_clone_depth = f.free_locals_clone_depth;
4447 f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len);
4448
4449 {
4450 defer {
4451 f.free_locals_clone_depth = pre_clone_depth;
4452 f.value_map.deinit();
4453 free_locals = f.getFreeLocals();
4454 deinitFreeLocalsMap(gpa, free_locals);
4455 f.value_map = old_value_map;
4456 free_locals.* = old_free_locals;
4457 }
4458
4459 for (liveness.deaths[case_i]) |operand| {
4460 try die(f, inst, Air.indexToRef(operand));
4461 }
4462
4463 try genBody(f, case_body);
4464 }
4465
4466 try noticeBranchFrees(f, pre_locals_len, inst);
4467 } else {
4468 for (liveness.deaths[case_i]) |operand| {
4469 try die(f, inst, Air.indexToRef(operand));
4470 }
4471 try genBody(f, case_body);
4472 }
4473
41584474 // The case body must be noreturn so we don't need to insert a break.
4159 try genBody(f, case_body);
4475
41604476 }
41614477
41624478 const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len];
41634479 try f.object.indent_writer.insertNewline();
4164 try writer.writeAll("default: ");
4165 try genBody(f, else_body);
4480 if (else_body.len > 0) {
4481 for (liveness.deaths[liveness.deaths.len - 1]) |operand| {
4482 try die(f, inst, Air.indexToRef(operand));
4483 }
4484 try writer.writeAll("default: ");
4485 try genBody(f, else_body);
4486 } else {
4487 try writer.writeAll("default: zig_unreachable();");
4488 }
41664489 try f.object.indent_writer.insertNewline();
41674490
41684491 f.object.indent_writer.popIndent();
......@@ -4189,235 +4512,268 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
41894512 const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]);
41904513 extra_i += inputs.len;
41914514
4192 if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none;
4193
4194 const writer = f.object.writer();
4195 const inst_ty = f.air.typeOfIndex(inst);
4196 const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: {
4197 const local = try f.allocLocal(inst_ty, .Mut);
4198 if (f.wantSafety()) {
4199 try writer.writeAll(" = ");
4200 try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer);
4201 }
4202 try writer.writeAll(";\n");
4203 break :local local;
4204 } else .none;
4515 const result: CValue = r: {
4516 if (!is_volatile and f.liveness.isUnused(inst)) break :r CValue.none;
42054517
4206 const locals_begin = f.next_local_index;
4207 const constraints_extra_begin = extra_i;
4208 for (outputs) |output| {
4209 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4210 const constraint = std.mem.sliceTo(extra_bytes, 0);
4211 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4212 // This equation accounts for the fact that even if we have exactly 4 bytes
4213 // for the string, we still use the next u32 for the null terminator.
4214 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4215
4216 if (constraint.len < 2 or constraint[0] != '=' or
4217 (constraint[1] == '{' and constraint[constraint.len - 1] != '}'))
4218 {
4219 return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
4220 }
4221
4222 const is_reg = constraint[1] == '{';
4223 if (is_reg) {
4224 const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType();
4225 try writer.writeAll("register ");
4226 _ = try f.allocLocal(output_ty, .Mut);
4227 try writer.writeAll(" __asm(\"");
4228 try writer.writeAll(constraint["={".len .. constraint.len - "}".len]);
4229 try writer.writeAll("\")");
4518 const writer = f.object.writer();
4519 const inst_ty = f.air.typeOfIndex(inst);
4520 const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: {
4521 const local = try f.allocLocal(inst, inst_ty);
42304522 if (f.wantSafety()) {
4523 try f.writeCValue(writer, local, .Other);
42314524 try writer.writeAll(" = ");
4232 try f.writeCValue(writer, .{ .undef = output_ty }, .Initializer);
4525 try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer);
4526 try writer.writeAll(";\n");
4527 }
4528 break :local local;
4529 } else .none;
4530
4531 const locals_begin = @intCast(LocalIndex, f.locals.items.len);
4532 const constraints_extra_begin = extra_i;
4533 for (outputs) |output| {
4534 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4535 const constraint = std.mem.sliceTo(extra_bytes, 0);
4536 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4537 // This equation accounts for the fact that even if we have exactly 4 bytes
4538 // for the string, we still use the next u32 for the null terminator.
4539 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4540
4541 if (constraint.len < 2 or constraint[0] != '=' or
4542 (constraint[1] == '{' and constraint[constraint.len - 1] != '}'))
4543 {
4544 return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
42334545 }
4234 try writer.writeAll(";\n");
4235 }
4236 }
4237 for (inputs) |input| {
4238 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4239 const constraint = std.mem.sliceTo(extra_bytes, 0);
4240 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4241 // This equation accounts for the fact that even if we have exactly 4 bytes
4242 // for the string, we still use the next u32 for the null terminator.
4243 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4244
4245 if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or
4246 (constraint[0] == '{' and constraint[constraint.len - 1] != '}'))
4247 {
4248 return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
4249 }
42504546
4251 const is_reg = constraint[0] == '{';
4252 const input_val = try f.resolveInst(input);
4253 if (asmInputNeedsLocal(constraint, input_val)) {
4254 const input_ty = f.air.typeOf(input);
4255 if (is_reg) try writer.writeAll("register ");
4256 _ = try f.allocLocal(input_ty, .Const);
4547 const is_reg = constraint[1] == '{';
42574548 if (is_reg) {
4549 const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType();
4550 try writer.writeAll("register ");
4551 const alignment = 0;
4552 const local_value = try f.allocLocalValue(output_ty, alignment);
4553 try f.object.dg.renderTypeAndName(
4554 writer,
4555 output_ty,
4556 local_value,
4557 .Mut,
4558 alignment,
4559 .Complete,
4560 );
42584561 try writer.writeAll(" __asm(\"");
4259 try writer.writeAll(constraint["{".len .. constraint.len - "}".len]);
4562 try writer.writeAll(constraint["={".len .. constraint.len - "}".len]);
42604563 try writer.writeAll("\")");
4564 if (f.wantSafety()) {
4565 try writer.writeAll(" = ");
4566 try f.writeCValue(writer, .{ .undef = output_ty }, .Initializer);
4567 }
4568 try writer.writeAll(";\n");
42614569 }
4262 try writer.writeAll(" = ");
4263 try f.writeCValue(writer, input_val, .Initializer);
4264 try writer.writeAll(";\n");
42654570 }
4266 }
4267 {
4268 var clobber_i: u32 = 0;
4269 while (clobber_i < clobbers_len) : (clobber_i += 1) {
4270 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
4571 for (inputs) |input| {
4572 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4573 const constraint = std.mem.sliceTo(extra_bytes, 0);
4574 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
42714575 // This equation accounts for the fact that even if we have exactly 4 bytes
42724576 // for the string, we still use the next u32 for the null terminator.
4273 extra_i += clobber.len / 4 + 1;
4274 }
4275 }
4276 {
4277 const asm_source = mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len];
4577 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
42784578
4279 var stack = std.heap.stackFallback(256, f.object.dg.gpa);
4280 const allocator = stack.get();
4281 const fixed_asm_source = try allocator.alloc(u8, asm_source.len);
4282 defer allocator.free(fixed_asm_source);
4579 if (constraint.len < 1 or std.mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or
4580 (constraint[0] == '{' and constraint[constraint.len - 1] != '}'))
4581 {
4582 return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
4583 }
42834584
4284 var src_i: usize = 0;
4285 var dst_i: usize = 0;
4286 while (true) {
4287 const literal = mem.sliceTo(asm_source[src_i..], '%');
4288 src_i += literal.len;
4585 const is_reg = constraint[0] == '{';
4586 const input_val = try f.resolveInst(input);
4587 if (asmInputNeedsLocal(constraint, input_val)) {
4588 const input_ty = f.air.typeOf(input);
4589 if (is_reg) try writer.writeAll("register ");
4590 const alignment = 0;
4591 const local_value = try f.allocLocalValue(input_ty, alignment);
4592 try f.object.dg.renderTypeAndName(
4593 writer,
4594 input_ty,
4595 local_value,
4596 .Const,
4597 alignment,
4598 .Complete,
4599 );
4600 if (is_reg) {
4601 try writer.writeAll(" __asm(\"");
4602 try writer.writeAll(constraint["{".len .. constraint.len - "}".len]);
4603 try writer.writeAll("\")");
4604 }
4605 try writer.writeAll(" = ");
4606 try f.writeCValue(writer, input_val, .Initializer);
4607 try writer.writeAll(";\n");
4608 }
4609 }
4610 {
4611 var clobber_i: u32 = 0;
4612 while (clobber_i < clobbers_len) : (clobber_i += 1) {
4613 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
4614 // This equation accounts for the fact that even if we have exactly 4 bytes
4615 // for the string, we still use the next u32 for the null terminator.
4616 extra_i += clobber.len / 4 + 1;
4617 }
4618 }
4619
4620 {
4621 const asm_source = mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len];
42894622
4290 mem.copy(u8, fixed_asm_source[dst_i..], literal);
4291 dst_i += literal.len;
4623 var stack = std.heap.stackFallback(256, f.object.dg.gpa);
4624 const allocator = stack.get();
4625 const fixed_asm_source = try allocator.alloc(u8, asm_source.len);
4626 defer allocator.free(fixed_asm_source);
42924627
4293 if (src_i >= asm_source.len) break;
4628 var src_i: usize = 0;
4629 var dst_i: usize = 0;
4630 while (true) {
4631 const literal = mem.sliceTo(asm_source[src_i..], '%');
4632 src_i += literal.len;
42944633
4295 src_i += 1;
4296 if (src_i >= asm_source.len)
4297 return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source});
4634 mem.copy(u8, fixed_asm_source[dst_i..], literal);
4635 dst_i += literal.len;
42984636
4299 fixed_asm_source[dst_i] = '%';
4300 dst_i += 1;
4637 if (src_i >= asm_source.len) break;
43014638
4302 if (asm_source[src_i] != '[') {
4303 // This also handles %%
4304 fixed_asm_source[dst_i] = asm_source[src_i];
43054639 src_i += 1;
4640 if (src_i >= asm_source.len)
4641 return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source});
4642
4643 fixed_asm_source[dst_i] = '%';
43064644 dst_i += 1;
4307 continue;
4308 }
43094645
4310 const desc = mem.sliceTo(asm_source[src_i..], ']');
4311 if (mem.indexOfScalar(u8, desc, ':')) |colon| {
4312 const name = desc[0..colon];
4313 const modifier = desc[colon + 1 ..];
4646 if (asm_source[src_i] != '[') {
4647 // This also handles %%
4648 fixed_asm_source[dst_i] = asm_source[src_i];
4649 src_i += 1;
4650 dst_i += 1;
4651 continue;
4652 }
43144653
4315 mem.copy(u8, fixed_asm_source[dst_i..], modifier);
4316 dst_i += modifier.len;
4317 mem.copy(u8, fixed_asm_source[dst_i..], name);
4318 dst_i += name.len;
4654 const desc = mem.sliceTo(asm_source[src_i..], ']');
4655 if (mem.indexOfScalar(u8, desc, ':')) |colon| {
4656 const name = desc[0..colon];
4657 const modifier = desc[colon + 1 ..];
43194658
4320 src_i += desc.len;
4321 if (src_i >= asm_source.len)
4322 return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source});
4659 mem.copy(u8, fixed_asm_source[dst_i..], modifier);
4660 dst_i += modifier.len;
4661 mem.copy(u8, fixed_asm_source[dst_i..], name);
4662 dst_i += name.len;
4663
4664 src_i += desc.len;
4665 if (src_i >= asm_source.len)
4666 return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source});
4667 }
43234668 }
4669
4670 try writer.writeAll("__asm");
4671 if (is_volatile) try writer.writeAll(" volatile");
4672 try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i])});
43244673 }
43254674
4326 try writer.writeAll("__asm");
4327 if (is_volatile) try writer.writeAll(" volatile");
4328 try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i])});
4329 }
4330
4331 extra_i = constraints_extra_begin;
4332 var locals_index = locals_begin;
4333 try writer.writeByte(':');
4334 for (outputs) |output, index| {
4335 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4336 const constraint = std.mem.sliceTo(extra_bytes, 0);
4337 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4338 // This equation accounts for the fact that even if we have exactly 4 bytes
4339 // for the string, we still use the next u32 for the null terminator.
4340 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4341
4342 if (index > 0) try writer.writeByte(',');
4343 try writer.writeByte(' ');
4344 if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
4345 const is_reg = constraint[1] == '{';
4346 try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint)});
4347 if (is_reg) {
4348 try f.writeCValue(writer, .{ .local = locals_index }, .Other);
4349 locals_index += 1;
4350 } else if (output == .none) {
4351 try f.writeCValue(writer, local, .FunctionArgument);
4352 } else {
4353 try f.writeCValueDeref(writer, try f.resolveInst(output));
4675 extra_i = constraints_extra_begin;
4676 var locals_index = locals_begin;
4677 try writer.writeByte(':');
4678 for (outputs) |output, index| {
4679 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4680 const constraint = std.mem.sliceTo(extra_bytes, 0);
4681 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4682 // This equation accounts for the fact that even if we have exactly 4 bytes
4683 // for the string, we still use the next u32 for the null terminator.
4684 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4685
4686 if (index > 0) try writer.writeByte(',');
4687 try writer.writeByte(' ');
4688 if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
4689 const is_reg = constraint[1] == '{';
4690 try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint)});
4691 if (is_reg) {
4692 try f.writeCValue(writer, .{ .local = locals_index }, .Other);
4693 locals_index += 1;
4694 } else if (output == .none) {
4695 try f.writeCValue(writer, local, .FunctionArgument);
4696 } else {
4697 try f.writeCValueDeref(writer, try f.resolveInst(output));
4698 }
4699 try writer.writeByte(')');
43544700 }
4355 try writer.writeByte(')');
4356 }
4357 try writer.writeByte(':');
4358 for (inputs) |input, index| {
4359 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4360 const constraint = std.mem.sliceTo(extra_bytes, 0);
4361 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4362 // This equation accounts for the fact that even if we have exactly 4 bytes
4363 // for the string, we still use the next u32 for the null terminator.
4364 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4365
4366 if (index > 0) try writer.writeByte(',');
4367 try writer.writeByte(' ');
4368 if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
4369
4370 const is_reg = constraint[0] == '{';
4371 const input_val = try f.resolveInst(input);
4372 try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint)});
4373 try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: {
4374 const input_local = CValue{ .local = locals_index };
4375 locals_index += 1;
4376 break :local input_local;
4377 } else input_val, .Other);
4378 try writer.writeByte(')');
4379 }
4380 try writer.writeByte(':');
4381 {
4382 var clobber_i: u32 = 0;
4383 while (clobber_i < clobbers_len) : (clobber_i += 1) {
4384 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
4701 try writer.writeByte(':');
4702 for (inputs) |input, index| {
4703 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4704 const constraint = std.mem.sliceTo(extra_bytes, 0);
4705 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
43854706 // This equation accounts for the fact that even if we have exactly 4 bytes
43864707 // for the string, we still use the next u32 for the null terminator.
4387 extra_i += clobber.len / 4 + 1;
4708 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4709
4710 if (index > 0) try writer.writeByte(',');
4711 try writer.writeByte(' ');
4712 if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
4713
4714 const is_reg = constraint[0] == '{';
4715 const input_val = try f.resolveInst(input);
4716 try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint)});
4717 try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: {
4718 const input_local = CValue{ .local = locals_index };
4719 locals_index += 1;
4720 break :local input_local;
4721 } else input_val, .Other);
4722 try writer.writeByte(')');
4723 }
4724 try writer.writeByte(':');
4725 {
4726 var clobber_i: u32 = 0;
4727 while (clobber_i < clobbers_len) : (clobber_i += 1) {
4728 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
4729 // This equation accounts for the fact that even if we have exactly 4 bytes
4730 // for the string, we still use the next u32 for the null terminator.
4731 extra_i += clobber.len / 4 + 1;
43884732
4389 if (clobber.len == 0) continue;
4733 if (clobber.len == 0) continue;
43904734
4391 if (clobber_i > 0) try writer.writeByte(',');
4392 try writer.print(" {s}", .{fmtStringLiteral(clobber)});
4735 if (clobber_i > 0) try writer.writeByte(',');
4736 try writer.print(" {s}", .{fmtStringLiteral(clobber)});
4737 }
43934738 }
4394 }
4395 try writer.writeAll(");\n");
4739 try writer.writeAll(");\n");
43964740
4397 extra_i = constraints_extra_begin;
4398 locals_index = locals_begin;
4399 for (outputs) |output| {
4400 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4401 const constraint = std.mem.sliceTo(extra_bytes, 0);
4402 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4403 // This equation accounts for the fact that even if we have exactly 4 bytes
4404 // for the string, we still use the next u32 for the null terminator.
4405 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4406
4407 const is_reg = constraint[1] == '{';
4408 if (is_reg) {
4409 try f.writeCValueDeref(writer, if (output == .none)
4410 CValue{ .local_ref = local.local }
4411 else
4412 try f.resolveInst(output));
4413 try writer.writeAll(" = ");
4414 try f.writeCValue(writer, .{ .local = locals_index }, .Other);
4415 locals_index += 1;
4416 try writer.writeAll(";\n");
4741 extra_i = constraints_extra_begin;
4742 locals_index = locals_begin;
4743 for (outputs) |output| {
4744 const extra_bytes = std.mem.sliceAsBytes(f.air.extra[extra_i..]);
4745 const constraint = std.mem.sliceTo(extra_bytes, 0);
4746 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
4747 // This equation accounts for the fact that even if we have exactly 4 bytes
4748 // for the string, we still use the next u32 for the null terminator.
4749 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
4750
4751 const is_reg = constraint[1] == '{';
4752 if (is_reg) {
4753 try f.writeCValueDeref(writer, if (output == .none)
4754 CValue{ .local_ref = local.local }
4755 else
4756 try f.resolveInst(output));
4757 try writer.writeAll(" = ");
4758 try f.writeCValue(writer, .{ .local = locals_index }, .Other);
4759 locals_index += 1;
4760 try writer.writeAll(";\n");
4761 }
44174762 }
4763
4764 break :r local;
4765 };
4766
4767 var bt = iterateBigTomb(f, inst);
4768 for (outputs) |output| {
4769 if (output == .none) continue;
4770 try bt.feed(output);
4771 }
4772 for (inputs) |input| {
4773 try bt.feed(input);
44184774 }
44194775
4420 return local;
4776 return result;
44214777}
44224778
44234779fn airIsNull(
......@@ -4425,12 +4781,26 @@ fn airIsNull(
44254781 inst: Air.Inst.Index,
44264782 operator: []const u8,
44274783 is_ptr: bool,
4428) !void {
4784) !CValue {
44294785 const un_op = f.air.instructions.items(.data)[inst].un_op;
4786
4787 if (f.liveness.isUnused(inst)) {
4788 try reap(f, inst, &.{un_op});
4789 return CValue.none;
4790 }
4791
44304792 const writer = f.object.writer();
44314793 const operand = try f.resolveInst(un_op);
4794 try reap(f, inst, &.{un_op});
44324795
4433 try if (is_ptr) f.writeCValueDeref(writer, operand) else f.writeCValue(writer, operand, .Other);
4796 const local = try f.allocLocal(inst, Type.bool);
4797 try f.writeCValue(writer, local, .Other);
4798 try writer.writeAll(" = ");
4799 if (is_ptr) {
4800 try f.writeCValueDeref(writer, operand);
4801 } else {
4802 try f.writeCValue(writer, operand, .Other);
4803 }
44344804
44354805 const operand_ty = f.air.typeOf(un_op);
44364806 const optional_ty = if (is_ptr) operand_ty.childType() else operand_ty;
......@@ -4457,33 +4827,52 @@ fn airIsNull(
44574827 try writer.writeAll(operator);
44584828 try writer.writeByte(' ');
44594829 try f.object.dg.renderValue(writer, rhs.ty, rhs.val, .Other);
4830 try writer.writeAll(";\n");
4831 return local;
44604832}
44614833
44624834fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
4463 if (f.liveness.isUnused(inst)) return CValue.none;
4464
44654835 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
4836
4837 if (f.liveness.isUnused(inst)) {
4838 try reap(f, inst, &.{ty_op.operand});
4839 return CValue.none;
4840 }
4841
44664842 const operand = try f.resolveInst(ty_op.operand);
4843 try reap(f, inst, &.{ty_op.operand});
44674844 const opt_ty = f.air.typeOf(ty_op.operand);
44684845
44694846 var buf: Type.Payload.ElemType = undefined;
44704847 const payload_ty = opt_ty.optionalChild(&buf);
44714848
4472 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
4473 if (opt_ty.optionalReprIsPayload()) return operand;
4849 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
4850 return CValue.none;
4851 }
44744852
44754853 const inst_ty = f.air.typeOfIndex(inst);
4854 const local = try f.allocLocal(inst, inst_ty);
4855 const writer = f.object.writer();
4856
4857 if (opt_ty.optionalReprIsPayload()) {
4858 try f.writeCValue(writer, local, .Other);
4859 try writer.writeAll(" = ");
4860 try f.writeCValue(writer, operand, .Other);
4861 try writer.writeAll(";\n");
4862 return local;
4863 }
4864
44764865 const target = f.object.dg.module.getTarget();
44774866 const is_array = lowersToArray(inst_ty, target);
44784867
4479 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
4480 const writer = f.object.writer();
44814868 if (is_array) {
4482 try writer.writeAll(";\n");
44834869 try writer.writeAll("memcpy(");
44844870 try f.writeCValue(writer, local, .FunctionArgument);
44854871 try writer.writeAll(", ");
4486 } else try writer.writeAll(" = ");
4872 } else {
4873 try f.writeCValue(writer, local, .Other);
4874 try writer.writeAll(" = ");
4875 }
44874876 try f.writeCValueMember(writer, operand, .{ .identifier = "payload" });
44884877 if (is_array) {
44894878 try writer.writeAll(", sizeof(");
......@@ -4495,11 +4884,16 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue {
44954884}
44964885
44974886fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {
4498 if (f.liveness.isUnused(inst)) return CValue.none;
4499
45004887 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
4888
4889 if (f.liveness.isUnused(inst)) {
4890 try reap(f, inst, &.{ty_op.operand});
4891 return CValue.none;
4892 }
4893
45014894 const writer = f.object.writer();
45024895 const operand = try f.resolveInst(ty_op.operand);
4896 try reap(f, inst, &.{ty_op.operand});
45034897 const ptr_ty = f.air.typeOf(ty_op.operand);
45044898 const opt_ty = ptr_ty.childType();
45054899 const inst_ty = f.air.typeOfIndex(inst);
......@@ -4508,15 +4902,18 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {
45084902 return CValue{ .undef = inst_ty };
45094903 }
45104904
4905 const local = try f.allocLocal(inst, inst_ty);
4906 try f.writeCValue(writer, local, .Other);
4907
45114908 if (opt_ty.optionalReprIsPayload()) {
45124909 // the operand is just a regular pointer, no need to do anything special.
45134910 // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C
4514 return operand;
4911 try writer.writeAll(" = ");
4912 try f.writeCValue(writer, operand, .Other);
4913 } else {
4914 try writer.writeAll(" = &");
4915 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" });
45154916 }
4516
4517 const local = try f.allocLocal(inst_ty, .Const);
4518 try writer.writeAll(" = &");
4519 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" });
45204917 try writer.writeAll(";\n");
45214918 return local;
45224919}
......@@ -4525,62 +4922,88 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
45254922 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
45264923 const writer = f.object.writer();
45274924 const operand = try f.resolveInst(ty_op.operand);
4925 try reap(f, inst, &.{ty_op.operand});
45284926 const operand_ty = f.air.typeOf(ty_op.operand);
45294927
45304928 const opt_ty = operand_ty.elemType();
45314929
4930 const inst_ty = f.air.typeOfIndex(inst);
4931
45324932 if (opt_ty.optionalReprIsPayload()) {
4933 if (f.liveness.isUnused(inst)) {
4934 return CValue.none;
4935 }
4936 const local = try f.allocLocal(inst, inst_ty);
45334937 // The payload and the optional are the same value.
45344938 // Setting to non-null will be done when the payload is set.
4535 return operand;
4536 }
4537
4538 try f.writeCValueDeref(writer, operand);
4539 try writer.writeAll(".is_null = ");
4540 try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer);
4541 try writer.writeAll(";\n");
4939 try f.writeCValue(writer, local, .Other);
4940 try writer.writeAll(" = ");
4941 try f.writeCValue(writer, operand, .Other);
4942 try writer.writeAll(";\n");
4943 return local;
4944 } else {
4945 try f.writeCValueDeref(writer, operand);
4946 try writer.writeAll(".is_null = ");
4947 try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer);
4948 try writer.writeAll(";\n");
45424949
4543 const inst_ty = f.air.typeOfIndex(inst);
4544 const local = try f.allocLocal(inst_ty, .Const);
4545 try writer.writeAll(" = &");
4546 try f.writeCValueDeref(writer, operand);
4950 if (f.liveness.isUnused(inst)) {
4951 return CValue.none;
4952 }
45474953
4548 try writer.writeAll(".payload;\n");
4549 return local;
4954 const local = try f.allocLocal(inst, inst_ty);
4955 try f.writeCValue(writer, local, .Other);
4956 try writer.writeAll(" = &");
4957 try f.writeCValueDeref(writer, operand);
4958 try writer.writeAll(".payload;\n");
4959 return local;
4960 }
45504961}
45514962
45524963fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
4553 if (f.liveness.isUnused(inst))
4964 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
4965 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
4966
4967 if (f.liveness.isUnused(inst)) {
4968 try reap(f, inst, &.{extra.struct_operand});
45544969 // TODO this @as is needed because of a stage1 bug
45554970 return @as(CValue, CValue.none);
4971 }
45564972
4557 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
4558 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
45594973 const struct_ptr = try f.resolveInst(extra.struct_operand);
4974 try reap(f, inst, &.{extra.struct_operand});
45604975 const struct_ptr_ty = f.air.typeOf(extra.struct_operand);
45614976 return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, extra.field_index);
45624977}
45634978
45644979fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue {
4565 if (f.liveness.isUnused(inst))
4980 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
4981
4982 if (f.liveness.isUnused(inst)) {
4983 try reap(f, inst, &.{ty_op.operand});
45664984 // TODO this @as is needed because of a stage1 bug
45674985 return @as(CValue, CValue.none);
4986 }
45684987
4569 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
45704988 const struct_ptr = try f.resolveInst(ty_op.operand);
4989 try reap(f, inst, &.{ty_op.operand});
45714990 const struct_ptr_ty = f.air.typeOf(ty_op.operand);
45724991 return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index);
45734992}
45744993
45754994fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
4576 if (f.liveness.isUnused(inst)) return CValue.none;
4577
45784995 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
45794996 const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
45804997
4998 if (f.liveness.isUnused(inst)) {
4999 try reap(f, inst, &.{extra.field_ptr});
5000 return CValue.none;
5001 }
5002
45815003 const struct_ptr_ty = f.air.typeOfIndex(inst);
45825004 const field_ptr_ty = f.air.typeOf(extra.field_ptr);
45835005 const field_ptr_val = try f.resolveInst(extra.field_ptr);
5006 try reap(f, inst, &.{extra.field_ptr});
45845007
45855008 const target = f.object.dg.module.getTarget();
45865009 const struct_ty = struct_ptr_ty.childType();
......@@ -4597,7 +5020,8 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
45975020 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);
45985021
45995022 const writer = f.object.writer();
4600 const local = try f.allocLocal(struct_ptr_ty, .Const);
5023 const local = try f.allocLocal(inst, struct_ptr_ty);
5024 try f.writeCValue(writer, local, .Other);
46015025 try writer.writeAll(" = (");
46025026 try f.renderTypecast(writer, struct_ptr_ty);
46035027 try writer.writeAll(")&((");
......@@ -4618,7 +5042,8 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
46185042 // Ensure complete type definition is visible before accessing fields.
46195043 try f.renderType(std.io.null_writer, struct_ty);
46205044
4621 const local = try f.allocLocal(field_ptr_ty, .Const);
5045 const local = try f.allocLocal(inst, field_ptr_ty);
5046 try f.writeCValue(writer, local, .Other);
46225047 try writer.writeAll(" = (");
46235048 try f.renderTypecast(writer, field_ptr_ty);
46245049 try writer.writeByte(')');
......@@ -4706,16 +5131,23 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
47065131}
47075132
47085133fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
4709 if (f.liveness.isUnused(inst))
5134 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
5135 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
5136
5137 if (f.liveness.isUnused(inst)) {
5138 try reap(f, inst, &.{extra.struct_operand});
47105139 return CValue.none;
5140 }
47115141
47125142 const inst_ty = f.air.typeOfIndex(inst);
4713 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) return CValue.none;
5143 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) {
5144 try reap(f, inst, &.{extra.struct_operand});
5145 return CValue.none;
5146 }
47145147
4715 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
4716 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
47175148 const target = f.object.dg.module.getTarget();
47185149 const struct_byval = try f.resolveInst(extra.struct_operand);
5150 try reap(f, inst, &.{extra.struct_operand});
47195151 const struct_ty = f.air.typeOf(extra.struct_operand);
47205152 const writer = f.object.writer();
47215153
......@@ -4759,7 +5191,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
47595191 };
47605192 const field_int_ty = Type.initPayload(&field_int_pl.base);
47615193
4762 const temp_local = try f.allocLocal(field_int_ty, .Const);
5194 const temp_local = try f.allocLocal(inst, try field_int_ty.copy(f.arena.allocator()));
5195 try f.writeCValue(writer, temp_local, .Other);
47635196 try writer.writeAll(" = zig_wrap_");
47645197 try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty);
47655198 try writer.writeAll("((");
......@@ -4775,8 +5208,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
47755208 try writer.writeAll(");\n");
47765209 if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local;
47775210
4778 const local = try f.allocLocal(inst_ty, .Mut);
4779 try writer.writeAll(";\n");
5211 const local = try f.allocLocal(inst, inst_ty);
47805212 try writer.writeAll("memcpy(");
47815213 try f.writeCValue(writer, .{ .local_ref = local.local }, .FunctionArgument);
47825214 try writer.writeAll(", ");
......@@ -4784,20 +5216,21 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
47845216 try writer.writeAll(", sizeof(");
47855217 try f.renderTypecast(writer, inst_ty);
47865218 try writer.writeAll("));\n");
5219 try freeLocal(f, inst, temp_local.local, 0);
47875220 return local;
47885221 },
47895222 },
47905223 .@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout() == .Packed) {
47915224 const operand_lval = if (struct_byval == .constant) blk: {
4792 const operand_local = try f.allocLocal(struct_ty, .Const);
5225 const operand_local = try f.allocLocal(inst, struct_ty);
5226 try f.writeCValue(writer, operand_local, .Other);
47935227 try writer.writeAll(" = ");
47945228 try f.writeCValue(writer, struct_byval, .Initializer);
47955229 try writer.writeAll(";\n");
47965230 break :blk operand_local;
47975231 } else struct_byval;
47985232
4799 const local = try f.allocLocal(inst_ty, .Mut);
4800 try writer.writeAll(";\n");
5233 const local = try f.allocLocal(inst, inst_ty);
48015234 try writer.writeAll("memcpy(&");
48025235 try f.writeCValue(writer, local, .FunctionArgument);
48035236 try writer.writeAll(", &");
......@@ -4805,6 +5238,11 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
48055238 try writer.writeAll(", sizeof(");
48065239 try f.renderTypecast(writer, inst_ty);
48075240 try writer.writeAll("));\n");
5241
5242 if (struct_byval == .constant) {
5243 try freeLocal(f, inst, operand_lval.local, 0);
5244 }
5245
48085246 return local;
48095247 } else .{
48105248 .identifier = struct_ty.unionFields().keys()[extra.field_index],
......@@ -4822,13 +5260,15 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
48225260 };
48235261
48245262 const is_array = lowersToArray(inst_ty, target);
4825 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
5263 const local = try f.allocLocal(inst, inst_ty);
48265264 if (is_array) {
4827 try writer.writeAll(";\n");
48285265 try writer.writeAll("memcpy(");
48295266 try f.writeCValue(writer, local, .FunctionArgument);
48305267 try writer.writeAll(", ");
4831 } else try writer.writeAll(" = ");
5268 } else {
5269 try f.writeCValue(writer, local, .Other);
5270 try writer.writeAll(" = ");
5271 }
48325272 if (extra_name != .none) {
48335273 try f.writeCValueMember(writer, struct_byval, extra_name);
48345274 try writer.writeByte('.');
......@@ -4846,40 +5286,53 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
48465286/// *(E!T) -> E
48475287/// Note that the result is never a pointer.
48485288fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
4849 if (f.liveness.isUnused(inst)) return CValue.none;
4850
48515289 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5290
5291 if (f.liveness.isUnused(inst)) {
5292 try reap(f, inst, &.{ty_op.operand});
5293 return CValue.none;
5294 }
5295
48525296 const inst_ty = f.air.typeOfIndex(inst);
48535297 const operand = try f.resolveInst(ty_op.operand);
48545298 const operand_ty = f.air.typeOf(ty_op.operand);
5299 try reap(f, inst, &.{ty_op.operand});
48555300
48565301 const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer;
48575302 const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
48585303 const error_ty = error_union_ty.errorUnionSet();
48595304 const payload_ty = error_union_ty.errorUnionPayload();
4860 if (!payload_ty.hasRuntimeBits()) return operand;
4861
5305 const local = try f.allocLocal(inst, inst_ty);
48625306 const writer = f.object.writer();
4863 const local = try f.allocLocal(inst_ty, .Const);
5307 try f.writeCValue(writer, local, .Other);
48645308 try writer.writeAll(" = ");
4865 if (!error_ty.errorSetIsEmpty())
4866 if (operand_is_ptr)
4867 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" })
5309
5310 if (!payload_ty.hasRuntimeBits()) {
5311 try f.writeCValue(writer, operand, .Other);
5312 } else {
5313 if (!error_ty.errorSetIsEmpty())
5314 if (operand_is_ptr)
5315 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" })
5316 else
5317 try f.writeCValueMember(writer, operand, .{ .identifier = "error" })
48685318 else
4869 try f.writeCValueMember(writer, operand, .{ .identifier = "error" })
4870 else
4871 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer);
5319 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer);
5320 }
48725321 try writer.writeAll(";\n");
48735322 return local;
48745323}
48755324
48765325fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
4877 if (f.liveness.isUnused(inst))
5326 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5327
5328 if (f.liveness.isUnused(inst)) {
5329 try reap(f, inst, &.{ty_op.operand});
48785330 return CValue.none;
5331 }
48795332
4880 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
48815333 const inst_ty = f.air.typeOfIndex(inst);
48825334 const operand = try f.resolveInst(ty_op.operand);
5335 try reap(f, inst, &.{ty_op.operand});
48835336 const operand_ty = f.air.typeOf(ty_op.operand);
48845337 const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer;
48855338 const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
......@@ -4887,8 +5340,9 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
48875340 if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) {
48885341 if (!is_ptr) return CValue.none;
48895342
4890 const local = try f.allocLocal(inst_ty, .Const);
48915343 const w = f.object.writer();
5344 const local = try f.allocLocal(inst, inst_ty);
5345 try f.writeCValue(w, local, .Other);
48925346 try w.writeAll(" = (");
48935347 try f.renderTypecast(w, inst_ty);
48945348 try w.writeByte(')');
......@@ -4898,7 +5352,8 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
48985352 }
48995353
49005354 const writer = f.object.writer();
4901 const local = try f.allocLocal(inst_ty, .Const);
5355 const local = try f.allocLocal(inst, inst_ty);
5356 try f.writeCValue(writer, local, .Other);
49025357 try writer.writeAll(" = ");
49035358 if (is_ptr) try writer.writeByte('&');
49045359 if (operand_is_ptr)
......@@ -4910,24 +5365,40 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
49105365}
49115366
49125367fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
4913 if (f.liveness.isUnused(inst)) return CValue.none;
5368 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5369
5370 if (f.liveness.isUnused(inst)) {
5371 try reap(f, inst, &.{ty_op.operand});
5372 return CValue.none;
5373 }
49145374
49155375 const inst_ty = f.air.typeOfIndex(inst);
4916 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
49175376 const payload = try f.resolveInst(ty_op.operand);
4918 if (inst_ty.optionalReprIsPayload()) return payload;
5377 try reap(f, inst, &.{ty_op.operand});
5378 const writer = f.object.writer();
5379
5380 if (inst_ty.optionalReprIsPayload()) {
5381 const local = try f.allocLocal(inst, inst_ty);
5382 try f.writeCValue(writer, local, .Other);
5383 try writer.writeAll(" = ");
5384 try f.writeCValue(writer, payload, .Other);
5385 try writer.writeAll(";\n");
5386 return local;
5387 }
49195388
49205389 const payload_ty = f.air.typeOf(ty_op.operand);
49215390 const target = f.object.dg.module.getTarget();
49225391 const is_array = lowersToArray(payload_ty, target);
49235392
4924 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
4925 const writer = f.object.writer();
4926 try writer.writeAll(" = { .payload = ");
4927 try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else payload, .Initializer);
4928 try writer.writeAll(", .is_null = ");
4929 try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer);
4930 try writer.writeAll(" };\n");
5393 const local = try f.allocLocal(inst, inst_ty);
5394 if (!is_array) {
5395 try f.writeCValue(writer, local, .Other);
5396 try writer.writeAll(".payload = ");
5397 try f.writeCValue(writer, payload, .Other);
5398 try writer.writeAll("; ");
5399 }
5400 try f.writeCValue(writer, local, .Other);
5401 try writer.writeAll(".is_null = false;\n");
49315402 if (is_array) {
49325403 try writer.writeAll("memcpy(");
49335404 try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
......@@ -4941,21 +5412,35 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
49415412}
49425413
49435414fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
4944 if (f.liveness.isUnused(inst)) return CValue.none;
5415 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5416 if (f.liveness.isUnused(inst)) {
5417 try reap(f, inst, &.{ty_op.operand});
5418 return CValue.none;
5419 }
49455420
49465421 const writer = f.object.writer();
4947 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
49485422 const operand = try f.resolveInst(ty_op.operand);
5423 try reap(f, inst, &.{ty_op.operand});
49495424 const error_union_ty = f.air.typeOfIndex(inst);
49505425 const payload_ty = error_union_ty.errorUnionPayload();
4951 if (!payload_ty.hasRuntimeBits()) return operand;
4952
4953 const local = try f.allocLocal(error_union_ty, .Const);
4954 try writer.writeAll(" = { .payload = ");
4955 try f.writeCValue(writer, .{ .undef = payload_ty }, .Initializer);
4956 try writer.writeAll(", .error = ");
4957 try f.writeCValue(writer, operand, .Initializer);
4958 try writer.writeAll(" };\n");
5426 const local = try f.allocLocal(inst, error_union_ty);
5427
5428 if (!payload_ty.hasRuntimeBits()) {
5429 try f.writeCValue(writer, local, .Other);
5430 try writer.writeAll(" = ");
5431 try f.writeCValue(writer, operand, .Other);
5432 try writer.writeAll(";\n");
5433 return local;
5434 }
5435
5436 {
5437 // TODO: set the payload to undefined
5438 //try f.writeCValue(writer, local, .Other);
5439 }
5440 try f.writeCValue(writer, local, .Other);
5441 try writer.writeAll(".error = ");
5442 try f.writeCValue(writer, operand, .Other);
5443 try writer.writeAll(";\n");
49595444 return local;
49605445}
49615446
......@@ -4977,6 +5462,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
49775462
49785463 return operand;
49795464 }
5465 try reap(f, inst, &.{ty_op.operand});
49805466 try f.writeCValueDeref(writer, operand);
49815467 try writer.writeAll(".error = ");
49825468 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other);
......@@ -4985,7 +5471,8 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
49855471 // Then return the payload pointer (only if it is used)
49865472 if (f.liveness.isUnused(inst)) return CValue.none;
49875473
4988 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
5474 const local = try f.allocLocal(inst, f.air.typeOfIndex(inst));
5475 try f.writeCValue(writer, local, .Other);
49895476 try writer.writeAll(" = &(");
49905477 try f.writeCValueDeref(writer, operand);
49915478 try writer.writeAll(").payload;\n");
......@@ -5008,24 +5495,30 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {
50085495}
50095496
50105497fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
5011 if (f.liveness.isUnused(inst)) return CValue.none;
5498 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5499 if (f.liveness.isUnused(inst)) {
5500 try reap(f, inst, &.{ty_op.operand});
5501 return CValue.none;
5502 }
50125503
50135504 const inst_ty = f.air.typeOfIndex(inst);
5014 const error_ty = inst_ty.errorUnionSet();
5015 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
50165505 const payload_ty = inst_ty.errorUnionPayload();
50175506 const payload = try f.resolveInst(ty_op.operand);
5507 try reap(f, inst, &.{ty_op.operand});
50185508
50195509 const target = f.object.dg.module.getTarget();
50205510 const is_array = lowersToArray(payload_ty, target);
50215511
5022 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
50235512 const writer = f.object.writer();
5024 try writer.writeAll(" = { .payload = ");
5025 try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else payload, .Initializer);
5026 try writer.writeAll(", .error = ");
5027 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer);
5028 try writer.writeAll(" };\n");
5513 const local = try f.allocLocal(inst, inst_ty);
5514 if (!is_array) {
5515 try f.writeCValue(writer, local, .Other);
5516 try writer.writeAll(".payload = ");
5517 try f.writeCValue(writer, payload, .Other);
5518 try writer.writeAll("; ");
5519 }
5520 try f.writeCValue(writer, local, .Other);
5521 try writer.writeAll(".error = 0;\n");
50295522 if (is_array) {
50305523 try writer.writeAll("memcpy(");
50315524 try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
......@@ -5038,15 +5531,26 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
50385531 return local;
50395532}
50405533
5041fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !void {
5534fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue {
50425535 const un_op = f.air.instructions.items(.data)[inst].un_op;
5536
5537 if (f.liveness.isUnused(inst)) {
5538 try reap(f, inst, &.{un_op});
5539 return CValue.none;
5540 }
5541
50435542 const writer = f.object.writer();
50445543 const operand = try f.resolveInst(un_op);
5544 try reap(f, inst, &.{un_op});
50455545 const operand_ty = f.air.typeOf(un_op);
5546 const local = try f.allocLocal(inst, Type.bool);
50465547 const err_union_ty = if (is_ptr) operand_ty.childType() else operand_ty;
50475548 const payload_ty = err_union_ty.errorUnionPayload();
50485549 const error_ty = err_union_ty.errorUnionSet();
50495550
5551 try f.writeCValue(writer, local, .Other);
5552 try writer.writeAll(" = ");
5553
50505554 if (!error_ty.errorSetIsEmpty())
50515555 if (payload_ty.hasRuntimeBits())
50525556 if (is_ptr)
......@@ -5061,20 +5565,27 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const
50615565 try writer.writeAll(operator);
50625566 try writer.writeByte(' ');
50635567 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other);
5568 try writer.writeAll(";\n");
5569 return local;
50645570}
50655571
50665572fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
5067 if (f.liveness.isUnused(inst))
5573 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5574
5575 if (f.liveness.isUnused(inst)) {
5576 try reap(f, inst, &.{ty_op.operand});
50685577 return CValue.none;
5578 }
50695579
5580 const operand = try f.resolveInst(ty_op.operand);
5581 try reap(f, inst, &.{ty_op.operand});
50705582 const inst_ty = f.air.typeOfIndex(inst);
5071 const local = try f.allocLocal(inst_ty, .Const);
5072 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
50735583 const writer = f.object.writer();
5074 const operand = try f.resolveInst(ty_op.operand);
5584 const local = try f.allocLocal(inst, inst_ty);
5585 try f.writeCValue(writer, local, .Other);
50755586 const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
50765587
5077 try writer.writeAll(" = { .ptr = ");
5588 try writer.writeAll(".ptr = ");
50785589 if (operand == .undef) {
50795590 // Unfortunately, C does not support any equivalent to
50805591 // &(*(void *)p)[0], although LLVM does via GetElementPtr
......@@ -5088,16 +5599,23 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
50885599
50895600 var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len };
50905601 const len_val = Value.initPayload(&len_pl.base);
5091 try writer.print(", .len = {} }};\n", .{try f.fmtIntLiteral(Type.usize, len_val)});
5602 try writer.writeAll("; ");
5603 try f.writeCValue(writer, local, .Other);
5604 try writer.print(".len = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)});
50925605 return local;
50935606}
50945607
50955608fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
5096 if (f.liveness.isUnused(inst)) return CValue.none;
5609 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5610
5611 if (f.liveness.isUnused(inst)) {
5612 try reap(f, inst, &.{ty_op.operand});
5613 return CValue.none;
5614 }
50975615
50985616 const inst_ty = f.air.typeOfIndex(inst);
5099 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
51005617 const operand = try f.resolveInst(ty_op.operand);
5618 try reap(f, inst, &.{ty_op.operand});
51015619 const operand_ty = f.air.typeOf(ty_op.operand);
51025620 const target = f.object.dg.module.getTarget();
51035621 const operation = if (inst_ty.isRuntimeFloat() and operand_ty.isRuntimeFloat())
......@@ -5109,8 +5627,9 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
51095627 else
51105628 unreachable;
51115629
5112 const local = try f.allocLocal(inst_ty, .Const);
51135630 const writer = f.object.writer();
5631 const local = try f.allocLocal(inst, inst_ty);
5632 try f.writeCValue(writer, local, .Other);
51145633
51155634 try writer.writeAll(" = ");
51165635 if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) {
......@@ -5134,16 +5653,27 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
51345653 return local;
51355654}
51365655
5137fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !void {
5138 const inst_ty = f.air.typeOfIndex(inst);
5656fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
51395657 const un_op = f.air.instructions.items(.data)[inst].un_op;
5140 const writer = f.object.writer();
5658
5659 if (f.liveness.isUnused(inst)) {
5660 try reap(f, inst, &.{un_op});
5661 return CValue.none;
5662 }
5663
51415664 const operand = try f.resolveInst(un_op);
5665 try reap(f, inst, &.{un_op});
5666 const inst_ty = f.air.typeOfIndex(inst);
5667 const writer = f.object.writer();
5668 const local = try f.allocLocal(inst, inst_ty);
5669 try f.writeCValue(writer, local, .Other);
51425670
5143 try writer.writeAll("(");
5671 try writer.writeAll(" = (");
51445672 try f.renderTypecast(writer, inst_ty);
51455673 try writer.writeByte(')');
51465674 try f.writeCValue(writer, operand, .Other);
5675 try writer.writeAll(";\n");
5676 return local;
51475677}
51485678
51495679fn airUnBuiltinCall(
......@@ -5151,19 +5681,31 @@ fn airUnBuiltinCall(
51515681 inst: Air.Inst.Index,
51525682 operation: []const u8,
51535683 info: BuiltinInfo,
5154) !void {
5155 const operand = f.air.instructions.items(.data)[inst].ty_op.operand;
5156 const operand_ty = f.air.typeOf(operand);
5684) !CValue {
5685 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5686
5687 if (f.liveness.isUnused(inst)) {
5688 try reap(f, inst, &.{ty_op.operand});
5689 return CValue.none;
5690 }
5691
5692 const operand = try f.resolveInst(ty_op.operand);
5693 try reap(f, inst, &.{ty_op.operand});
5694 const inst_ty = f.air.typeOfIndex(inst);
5695 const operand_ty = f.air.typeOf(ty_op.operand);
51575696
51585697 const writer = f.object.writer();
5159 try writer.writeAll("zig_");
5698 const local = try f.allocLocal(inst, inst_ty);
5699 try f.writeCValue(writer, local, .Other);
5700 try writer.writeAll(" = zig_");
51605701 try writer.writeAll(operation);
51615702 try writer.writeByte('_');
51625703 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
51635704 try writer.writeByte('(');
5164 try f.writeCValue(writer, try f.resolveInst(operand), .FunctionArgument);
5705 try f.writeCValue(writer, operand, .FunctionArgument);
51655706 try f.object.dg.renderBuiltinInfo(writer, operand_ty, info);
5166 try writer.writeAll(")");
5707 try writer.writeAll(");\n");
5708 return local;
51675709}
51685710
51695711fn airBinBuiltinCall(
......@@ -5171,99 +5713,131 @@ fn airBinBuiltinCall(
51715713 inst: Air.Inst.Index,
51725714 operation: []const u8,
51735715 info: BuiltinInfo,
5174) !void {
5716) !CValue {
51755717 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
5718
5719 if (f.liveness.isUnused(inst)) {
5720 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
5721 return CValue.none;
5722 }
5723
5724 const lhs = try f.resolveInst(bin_op.lhs);
5725 const rhs = try f.resolveInst(bin_op.rhs);
5726 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
5727
5728 const inst_ty = f.air.typeOfIndex(inst);
51765729 const operand_ty = f.air.typeOf(bin_op.lhs);
51775730
51785731 const writer = f.object.writer();
5179 try writer.writeAll("zig_");
5732 const local = try f.allocLocal(inst, inst_ty);
5733 try f.writeCValue(writer, local, .Other);
5734 try writer.writeAll(" = zig_");
51805735 try writer.writeAll(operation);
51815736 try writer.writeByte('_');
51825737 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
51835738 try writer.writeByte('(');
5184 try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument);
5739 try f.writeCValue(writer, lhs, .FunctionArgument);
51855740 try writer.writeAll(", ");
5186 try f.writeCValue(writer, try f.resolveInst(bin_op.rhs), .FunctionArgument);
5741 try f.writeCValue(writer, rhs, .FunctionArgument);
51875742 try f.object.dg.renderBuiltinInfo(writer, operand_ty, info);
5188 try writer.writeAll(")");
5743 try writer.writeAll(");\n");
5744 return local;
51895745}
51905746
5191fn airCmpBuiltinCall(
5747fn cmpBuiltinCall(
51925748 f: *Function,
51935749 inst: Air.Inst.Index,
51945750 operator: []const u8,
51955751 operation: []const u8,
5196) !void {
5752) !CValue {
5753 const inst_ty = f.air.typeOfIndex(inst);
51975754 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
51985755 const operand_ty = f.air.typeOf(bin_op.lhs);
51995756
5757 const lhs = try f.resolveInst(bin_op.lhs);
5758 const rhs = try f.resolveInst(bin_op.rhs);
5759 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
5760
52005761 const writer = f.object.writer();
5201 try writer.writeAll("zig_");
5762 const local = try f.allocLocal(inst, inst_ty);
5763 try f.writeCValue(writer, local, .Other);
5764 try writer.writeAll(" = zig_");
52025765 try writer.writeAll(operation);
52035766 try writer.writeByte('_');
52045767 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
52055768 try writer.writeByte('(');
5206 try f.writeCValue(writer, try f.resolveInst(bin_op.lhs), .FunctionArgument);
5769 try f.writeCValue(writer, lhs, .FunctionArgument);
52075770 try writer.writeAll(", ");
5208 try f.writeCValue(writer, try f.resolveInst(bin_op.rhs), .FunctionArgument);
5209 try writer.print(") {s} {}", .{ operator, try f.fmtIntLiteral(Type.initTag(.i32), Value.zero) });
5771 try f.writeCValue(writer, rhs, .FunctionArgument);
5772 try writer.print(") {s} {};\n", .{ operator, try f.fmtIntLiteral(Type.initTag(.i32), Value.zero) });
5773 return local;
52105774}
52115775
52125776fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
52135777 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
52145778 const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
52155779 const inst_ty = f.air.typeOfIndex(inst);
5216 const is_struct = !inst_ty.isPtrLikeOptional();
5217 const ptr_ty = f.air.typeOf(extra.ptr);
52185780 const ptr = try f.resolveInst(extra.ptr);
52195781 const expected_value = try f.resolveInst(extra.expected_value);
52205782 const new_value = try f.resolveInst(extra.new_value);
5783 try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value });
52215784 const writer = f.object.writer();
5222
5223 const local = try f.allocLocal(inst_ty, .Mut);
5224 try writer.writeAll(" = ");
5225 if (is_struct) try writer.writeAll("{ .payload = ");
5226 try f.writeCValue(writer, expected_value, .Initializer);
5227 if (is_struct) {
5228 try writer.writeAll(", .is_null = ");
5229 try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer);
5230 try writer.writeAll(" }");
5231 }
5232 try writer.writeAll(";\n");
5233
5234 if (is_struct) {
5785 const ptr_ty = f.air.typeOf(extra.ptr);
5786 const local = try f.allocLocal(inst, inst_ty);
5787 if (inst_ty.isPtrLikeOptional()) {
52355788 try f.writeCValue(writer, local, .Other);
5236 try writer.writeAll(".is_null = ");
5237 } else {
5789 try writer.writeAll(" = ");
5790 try f.writeCValue(writer, expected_value, .Initializer);
5791 try writer.writeAll(";\n");
52385792 try writer.writeAll("if (");
5239 }
5240 try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor});
5241 try f.renderTypecast(writer, ptr_ty.elemType());
5242 try writer.writeByte(')');
5243 if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile");
5244 try writer.writeAll(" *)");
5245 try f.writeCValue(writer, ptr, .Other);
5246 try writer.writeAll(", ");
5247 if (is_struct)
5248 try f.writeCValueMember(writer, local, .{ .identifier = "payload" })
5249 else
5793 try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor});
5794 try f.renderTypecast(writer, ptr_ty.elemType());
5795 try writer.writeByte(')');
5796 if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile");
5797 try writer.writeAll(" *)");
5798 try f.writeCValue(writer, ptr, .Other);
5799 try writer.writeAll(", ");
52505800 try f.writeCValue(writer, local, .FunctionArgument);
5251 try writer.writeAll(", ");
5252 try f.writeCValue(writer, new_value, .FunctionArgument);
5253 try writer.writeAll(", ");
5254 try writeMemoryOrder(writer, extra.successOrder());
5255 try writer.writeAll(", ");
5256 try writeMemoryOrder(writer, extra.failureOrder());
5257 try writer.writeByte(')');
5258 if (is_struct) {
5259 try writer.writeAll(";\n");
5260 } else {
5801 try writer.writeAll(", ");
5802 try f.writeCValue(writer, new_value, .FunctionArgument);
5803 try writer.writeAll(", ");
5804 try writeMemoryOrder(writer, extra.successOrder());
5805 try writer.writeAll(", ");
5806 try writeMemoryOrder(writer, extra.failureOrder());
5807 try writer.writeByte(')');
52615808 try writer.writeAll(") {\n");
52625809 f.object.indent_writer.pushIndent();
52635810 try f.writeCValue(writer, local, .Other);
52645811 try writer.writeAll(" = NULL;\n");
52655812 f.object.indent_writer.popIndent();
52665813 try writer.writeAll("}\n");
5814 } else {
5815 try f.writeCValue(writer, local, .Other);
5816 try writer.writeAll(".payload = ");
5817 try f.writeCValue(writer, expected_value, .Other);
5818 try writer.writeAll(";\n");
5819 try f.writeCValue(writer, local, .Other);
5820 try writer.print(".is_null = zig_cmpxchg_{s}((zig_atomic(", .{flavor});
5821 try f.renderTypecast(writer, ptr_ty.elemType());
5822 try writer.writeByte(')');
5823 if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile");
5824 try writer.writeAll(" *)");
5825 try f.writeCValue(writer, ptr, .Other);
5826 try writer.writeAll(", ");
5827 try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
5828 try writer.writeAll(", ");
5829 try f.writeCValue(writer, new_value, .FunctionArgument);
5830 try writer.writeAll(", ");
5831 try writeMemoryOrder(writer, extra.successOrder());
5832 try writer.writeAll(", ");
5833 try writeMemoryOrder(writer, extra.failureOrder());
5834 try writer.writeByte(')');
5835 try writer.writeAll(";\n");
5836 }
5837
5838 if (f.liveness.isUnused(inst)) {
5839 try freeLocal(f, inst, local.local, 0);
5840 return CValue.none;
52675841 }
52685842
52695843 return local;
......@@ -5276,8 +5850,10 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
52765850 const ptr_ty = f.air.typeOf(pl_op.operand);
52775851 const ptr = try f.resolveInst(pl_op.operand);
52785852 const operand = try f.resolveInst(extra.operand);
5279 const local = try f.allocLocal(inst_ty, .Const);
5853 try reap(f, inst, &.{ pl_op.operand, extra.operand });
52805854 const writer = f.object.writer();
5855 const local = try f.allocLocal(inst, inst_ty);
5856 try f.writeCValue(writer, local, .Other);
52815857
52825858 try writer.print(" = zig_atomicrmw_{s}((", .{toAtomicRmwSuffix(extra.op())});
52835859 switch (extra.op()) {
......@@ -5300,19 +5876,27 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
53005876 try writeMemoryOrder(writer, extra.ordering());
53015877 try writer.writeAll(");\n");
53025878
5879 if (f.liveness.isUnused(inst)) {
5880 try freeLocal(f, inst, local.local, 0);
5881 return CValue.none;
5882 }
5883
53035884 return local;
53045885}
53055886
53065887fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
53075888 const atomic_load = f.air.instructions.items(.data)[inst].atomic_load;
53085889 const ptr = try f.resolveInst(atomic_load.ptr);
5890 try reap(f, inst, &.{atomic_load.ptr});
53095891 const ptr_ty = f.air.typeOf(atomic_load.ptr);
5310 if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst))
5892 if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) {
53115893 return CValue.none;
5894 }
53125895
53135896 const inst_ty = f.air.typeOfIndex(inst);
5314 const local = try f.allocLocal(inst_ty, .Const);
53155897 const writer = f.object.writer();
5898 const local = try f.allocLocal(inst, inst_ty);
5899 try f.writeCValue(writer, local, .Other);
53165900
53175901 try writer.writeAll(" = zig_atomic_load((zig_atomic(");
53185902 try f.renderTypecast(writer, ptr_ty.elemType());
......@@ -5332,6 +5916,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
53325916 const ptr_ty = f.air.typeOf(bin_op.lhs);
53335917 const ptr = try f.resolveInst(bin_op.lhs);
53345918 const element = try f.resolveInst(bin_op.rhs);
5919 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
53355920 const writer = f.object.writer();
53365921
53375922 try writer.writeAll("zig_atomic_store((zig_atomic(");
......@@ -5354,6 +5939,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
53545939 const dest_ptr = try f.resolveInst(pl_op.operand);
53555940 const value = try f.resolveInst(extra.lhs);
53565941 const len = try f.resolveInst(extra.rhs);
5942 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
53575943
53585944 const writer = f.object.writer();
53595945 if (dest_ty.isVolatilePtr()) {
......@@ -5362,7 +5948,8 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
53625948 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);
53635949
53645950 try writer.writeAll("for (");
5365 const index = try f.allocLocal(Type.usize, .Mut);
5951 const index = try f.allocLocal(inst, Type.usize);
5952 try f.writeCValue(writer, index, .Other);
53665953 try writer.writeAll(" = ");
53675954 try f.object.dg.renderValue(writer, Type.usize, Value.zero, .Initializer);
53685955 try writer.writeAll("; ");
......@@ -5383,6 +5970,8 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
53835970 try f.writeCValue(writer, value, .FunctionArgument);
53845971 try writer.writeAll(";\n");
53855972
5973 try freeLocal(f, inst, index.local, 0);
5974
53865975 return CValue.none;
53875976 }
53885977
......@@ -5403,6 +5992,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
54035992 const dest_ptr = try f.resolveInst(pl_op.operand);
54045993 const src_ptr = try f.resolveInst(extra.lhs);
54055994 const len = try f.resolveInst(extra.rhs);
5995 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
54065996 const writer = f.object.writer();
54075997
54085998 try writer.writeAll("memcpy(");
......@@ -5420,6 +6010,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
54206010 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
54216011 const union_ptr = try f.resolveInst(bin_op.lhs);
54226012 const new_tag = try f.resolveInst(bin_op.rhs);
6013 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
54236014 const writer = f.object.writer();
54246015
54256016 const union_ty = f.air.typeOf(bin_op.lhs).childType();
......@@ -5436,53 +6027,94 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
54366027 return CValue.none;
54376028}
54386029
5439fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !void {
6030fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
54406031 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
5441 const un_ty = f.air.typeOf(ty_op.operand);
5442 const writer = f.object.writer();
6032
6033 if (f.liveness.isUnused(inst)) {
6034 try reap(f, inst, &.{ty_op.operand});
6035 return CValue.none;
6036 }
6037
54436038 const operand = try f.resolveInst(ty_op.operand);
6039 try reap(f, inst, &.{ty_op.operand});
6040
6041 const un_ty = f.air.typeOf(ty_op.operand);
54446042
54456043 const target = f.object.dg.module.getTarget();
54466044 const layout = un_ty.unionGetLayout(target);
5447 assert(layout.tag_size != 0);
6045 if (layout.tag_size == 0) return CValue.none;
54486046
6047 const inst_ty = f.air.typeOfIndex(inst);
6048 const writer = f.object.writer();
6049 const local = try f.allocLocal(inst, inst_ty);
6050 try f.writeCValue(writer, local, .Other);
6051
6052 try writer.writeAll(" = ");
54496053 try f.writeCValue(writer, operand, .Other);
5450 try writer.writeAll(".tag");
6054 try writer.writeAll(".tag;\n");
6055 return local;
54516056}
54526057
5453fn airTagName(f: *Function, inst: Air.Inst.Index) !void {
6058fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
54546059 const un_op = f.air.instructions.items(.data)[inst].un_op;
6060
6061 if (f.liveness.isUnused(inst)) {
6062 try reap(f, inst, &.{un_op});
6063 return CValue.none;
6064 }
6065
6066 const inst_ty = f.air.typeOfIndex(inst);
54556067 const enum_ty = f.air.typeOf(un_op);
54566068 const operand = try f.resolveInst(un_op);
6069 try reap(f, inst, &.{un_op});
54576070
54586071 const writer = f.object.writer();
5459 try writer.print("{s}(", .{try f.object.dg.getTagNameFn(enum_ty)});
6072 const local = try f.allocLocal(inst, inst_ty);
6073 try f.writeCValue(writer, local, .Other);
6074 try writer.print(" = {s}(", .{try f.object.dg.getTagNameFn(enum_ty)});
54606075 try f.writeCValue(writer, operand, .Other);
5461 try writer.writeAll(")");
6076 try writer.writeAll(");\n");
6077
6078 return local;
54626079}
54636080
5464fn airErrorName(f: *Function, inst: Air.Inst.Index) !void {
6081fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
54656082 const un_op = f.air.instructions.items(.data)[inst].un_op;
6083
6084 if (f.liveness.isUnused(inst)) {
6085 try reap(f, inst, &.{un_op});
6086 return CValue.none;
6087 }
6088
54666089 const writer = f.object.writer();
6090 const inst_ty = f.air.typeOfIndex(inst);
54676091 const operand = try f.resolveInst(un_op);
6092 try reap(f, inst, &.{un_op});
6093 const local = try f.allocLocal(inst, inst_ty);
6094 try f.writeCValue(writer, local, .Other);
54686095
5469 try writer.writeAll("zig_errorName[");
6096 try writer.writeAll(" = zig_errorName[");
54706097 try f.writeCValue(writer, operand, .Other);
5471 try writer.writeAll("]");
6098 try writer.writeAll("];\n");
6099 return local;
54726100}
54736101
54746102fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
5475 if (f.liveness.isUnused(inst)) return CValue.none;
6103 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
6104 if (f.liveness.isUnused(inst)) {
6105 try reap(f, inst, &.{ty_op.operand});
6106 return CValue.none;
6107 }
54766108
54776109 const inst_ty = f.air.typeOfIndex(inst);
5478 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
54796110 const operand = try f.resolveInst(ty_op.operand);
6111 try reap(f, inst, &.{ty_op.operand});
54806112 const writer = f.object.writer();
5481 const local = try f.allocLocal(inst_ty, .Const);
6113 const local = try f.allocLocal(inst, inst_ty);
6114 try f.writeCValue(writer, local, .Other);
54826115 try writer.writeAll(" = ");
54836116
54846117 _ = operand;
5485 _ = local;
54866118 return f.fail("TODO: C backend: implement airSplat", .{});
54876119}
54886120
......@@ -5499,12 +6131,17 @@ fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue {
54996131}
55006132
55016133fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
5502 if (f.liveness.isUnused(inst)) return CValue.none;
6134 const reduce = f.air.instructions.items(.data)[inst].reduce;
6135
6136 if (f.liveness.isUnused(inst)) {
6137 try reap(f, inst, &.{reduce.operand});
6138 return CValue.none;
6139 }
55036140
55046141 const target = f.object.dg.module.getTarget();
55056142 const scalar_ty = f.air.typeOfIndex(inst);
5506 const reduce = f.air.instructions.items(.data)[inst].reduce;
55076143 const operand = try f.resolveInst(reduce.operand);
6144 try reap(f, inst, &.{reduce.operand});
55086145 const operand_ty = f.air.typeOf(reduce.operand);
55096146 const vector_len = operand_ty.vectorLen();
55106147 const writer = f.object.writer();
......@@ -5581,10 +6218,12 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
55816218 // }
55826219 // break :reduce accum;
55836220 // }
5584 const it = try f.allocLocal(Type.usize, .Mut);
6221 const it = try f.allocLocal(inst, Type.usize);
6222 try f.writeCValue(writer, it, .Other);
55856223 try writer.writeAll(" = 0;\n");
55866224
5587 const accum = try f.allocLocal(scalar_ty, .Mut);
6225 const accum = try f.allocLocal(inst, scalar_ty);
6226 try f.writeCValue(writer, accum, .Other);
55886227 try writer.writeAll(" = ");
55896228
55906229 const init_val = switch (reduce.operation) {
......@@ -5604,7 +6243,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
56046243 try writer.writeAll(init_val);
56056244 try writer.writeAll(";");
56066245 try f.object.indent_writer.insertNewline();
5607 try writer.writeAll("for(;");
6246 try writer.writeAll("for (;");
56086247 try f.writeCValue(writer, it, .Other);
56096248 try writer.print("<{d};++", .{vector_len});
56106249 try f.writeCValue(writer, it, .Other);
......@@ -5647,44 +6286,57 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
56476286
56486287 try writer.writeAll(";\n");
56496288
6289 try freeLocal(f, inst, it.local, 0);
6290
56506291 return accum;
56516292}
56526293
56536294fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
5654 if (f.liveness.isUnused(inst)) return CValue.none;
5655
5656 const inst_ty = f.air.typeOfIndex(inst);
56576295 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
6296 const inst_ty = f.air.typeOfIndex(inst);
56586297 const len = @intCast(usize, inst_ty.arrayLen());
56596298 const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]);
6299 const gpa = f.object.dg.gpa;
6300 const resolved_elements = try gpa.alloc(CValue, elements.len);
6301 defer gpa.free(resolved_elements);
6302 for (elements) |element, i| {
6303 resolved_elements[i] = try f.resolveInst(element);
6304 }
6305 {
6306 var bt = iterateBigTomb(f, inst);
6307 for (elements) |element| {
6308 try bt.feed(element);
6309 }
6310 }
6311
6312 if (f.liveness.isUnused(inst)) return CValue.none;
6313
56606314 const target = f.object.dg.module.getTarget();
5661 const mutability: Mutability = for (elements) |element| {
5662 if (lowersToArray(f.air.typeOf(element), target)) break .Mut;
5663 } else .Const;
56646315
56656316 const writer = f.object.writer();
5666 const local = try f.allocLocal(inst_ty, mutability);
5667 try writer.writeAll(" = ");
6317 const local = try f.allocLocal(inst, inst_ty);
56686318 switch (inst_ty.zigTypeTag()) {
56696319 .Array, .Vector => {
56706320 const elem_ty = inst_ty.childType();
5671 try writer.writeByte('{');
5672 var empty = true;
5673 for (elements) |element| {
5674 if (!empty) try writer.writeAll(", ");
5675 try f.writeCValue(writer, try f.resolveInst(element), .Initializer);
5676 empty = false;
6321 for (resolved_elements) |element, i| {
6322 try f.writeCValue(writer, local, .Other);
6323 try writer.print("[{d}] = ", .{i});
6324 try f.writeCValue(writer, element, .Other);
6325 try writer.writeAll(";\n");
56776326 }
56786327 if (inst_ty.sentinel()) |sentinel| {
5679 if (!empty) try writer.writeAll(", ");
5680 try f.object.dg.renderValue(writer, elem_ty, sentinel, .Initializer);
5681 empty = false;
6328 try f.writeCValue(writer, local, .Other);
6329 try writer.print("[{d}] = ", .{resolved_elements.len});
6330 try f.object.dg.renderValue(writer, elem_ty, sentinel, .Other);
6331 try writer.writeAll(";\n");
56826332 }
5683 if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)});
5684 try writer.writeAll("};\n");
56856333 },
56866334 .Struct => switch (inst_ty.containerLayout()) {
56876335 .Auto, .Extern => {
6336 try f.writeCValue(writer, local, .Other);
6337 try writer.writeAll(" = (");
6338 try f.renderTypecast(writer, inst_ty);
6339 try writer.writeAll(")");
56886340 try writer.writeByte('{');
56896341 var empty = true;
56906342 for (elements) |element, index| {
......@@ -5698,7 +6350,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
56986350 const element_ty = f.air.typeOf(element);
56996351 try f.writeCValue(writer, switch (element_ty.zigTypeTag()) {
57006352 .Array => CValue{ .undef = element_ty },
5701 else => try f.resolveInst(element),
6353 else => resolved_elements[index],
57026354 }, .Initializer);
57036355 empty = false;
57046356 }
......@@ -5721,7 +6373,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
57216373 try writer.writeAll("memcpy(");
57226374 try f.writeCValueMember(writer, local, field_name);
57236375 try writer.writeAll(", ");
5724 try f.writeCValue(writer, try f.resolveInst(element), .FunctionArgument);
6376 try f.writeCValue(writer, resolved_elements[index], .FunctionArgument);
57256377 try writer.writeAll(", sizeof(");
57266378 try f.renderTypecast(writer, element_ty);
57276379 try writer.writeAll("));\n");
......@@ -5730,6 +6382,10 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
57306382 }
57316383 },
57326384 .Packed => {
6385 try f.writeCValue(writer, local, .Other);
6386 try writer.writeAll(" = (");
6387 try f.renderTypecast(writer, inst_ty);
6388 try writer.writeAll(")");
57336389 const int_info = inst_ty.intInfo(target);
57346390
57356391 var bit_offset_ty_pl = Type.Payload.Bits{
......@@ -5754,7 +6410,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
57546410 empty = false;
57556411 }
57566412 empty = true;
5757 for (elements) |element, index| {
6413 for (resolved_elements) |element, index| {
57586414 const field_ty = inst_ty.structFieldType(index);
57596415 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue;
57606416
......@@ -5772,7 +6428,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
57726428 });
57736429 try writer.writeByte(')');
57746430 }
5775 try f.writeCValue(writer, try f.resolveInst(element), .Other);
6431 try f.writeCValue(writer, element, .Other);
57766432 try writer.writeAll(", ");
57776433 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
57786434 try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits);
......@@ -5793,26 +6449,31 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
57936449}
57946450
57956451fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
5796 if (f.liveness.isUnused(inst)) return CValue.none;
5797
57986452 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
57996453 const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data;
6454
6455 if (f.liveness.isUnused(inst)) {
6456 try reap(f, inst, &.{extra.init});
6457 return CValue.none;
6458 }
6459
58006460 const union_ty = f.air.typeOfIndex(inst);
58016461 const target = f.object.dg.module.getTarget();
58026462 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
58036463 const field_name = union_obj.fields.keys()[extra.field_index];
58046464 const payload = try f.resolveInst(extra.init);
6465 try reap(f, inst, &.{extra.init});
58056466
58066467 const writer = f.object.writer();
5807 const local = try f.allocLocal(union_ty, .Const);
6468 const local = try f.allocLocal(inst, union_ty);
58086469 if (union_obj.layout == .Packed) {
6470 try f.writeCValue(writer, local, .Other);
58096471 try writer.writeAll(" = ");
58106472 try f.writeCValue(writer, payload, .Initializer);
58116473 try writer.writeAll(";\n");
58126474 return local;
58136475 }
58146476
5815 try writer.writeAll(" = {");
58166477 if (union_ty.unionTagTypeSafety()) |tag_ty| {
58176478 const layout = union_ty.unionGetLayout(target);
58186479 if (layout.tag_size != 0) {
......@@ -5827,16 +6488,20 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
58276488 var int_pl: Value.Payload.U64 = undefined;
58286489 const int_val = tag_val.enumToInt(tag_ty, &int_pl);
58296490
5830 try writer.print(".tag = {}, ", .{try f.fmtIntLiteral(tag_ty, int_val)});
6491 try f.writeCValue(writer, local, .Other);
6492 try writer.print(".tag = {}; ", .{try f.fmtIntLiteral(tag_ty, int_val)});
58316493 }
5832 try writer.writeAll(".payload = {");
6494 try f.writeCValue(writer, local, .Other);
6495 try writer.print(".payload.{ } = ", .{fmtIdent(field_name)});
6496 try f.writeCValue(writer, payload, .Other);
6497 try writer.writeAll(";\n");
6498 return local;
58336499 }
58346500
6501 try f.writeCValue(writer, local, .Other);
58356502 try writer.print(".{ } = ", .{fmtIdent(field_name)});
5836 try f.writeCValue(writer, payload, .Initializer);
5837
5838 if (union_ty.unionTagTypeSafety()) |_| try writer.writeByte('}');
5839 try writer.writeAll("};\n");
6503 try f.writeCValue(writer, payload, .Other);
6504 try writer.writeAll(";\n");
58406505
58416506 return local;
58426507}
......@@ -5851,6 +6516,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
58516516 .instruction => return CValue.none,
58526517 }
58536518 const ptr = try f.resolveInst(prefetch.ptr);
6519 try reap(f, inst, &.{prefetch.ptr});
58546520 const writer = f.object.writer();
58556521 try writer.writeAll("zig_prefetch(");
58566522 try f.writeCValue(writer, ptr, .FunctionArgument);
......@@ -5867,7 +6533,8 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
58676533
58686534 const writer = f.object.writer();
58696535 const inst_ty = f.air.typeOfIndex(inst);
5870 const local = try f.allocLocal(inst_ty, .Const);
6536 const local = try f.allocLocal(inst, inst_ty);
6537 try f.writeCValue(writer, local, .Other);
58716538
58726539 try writer.writeAll(" = ");
58736540 try writer.print("zig_wasm_memory_size({d});\n", .{pl_op.payload});
......@@ -5881,7 +6548,9 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
58816548 const writer = f.object.writer();
58826549 const inst_ty = f.air.typeOfIndex(inst);
58836550 const operand = try f.resolveInst(pl_op.operand);
5884 const local = try f.allocLocal(inst_ty, .Const);
6551 try reap(f, inst, &.{pl_op.operand});
6552 const local = try f.allocLocal(inst, inst_ty);
6553 try f.writeCValue(writer, local, .Other);
58856554
58866555 try writer.writeAll(" = ");
58876556 try writer.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload});
......@@ -5891,15 +6560,20 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
58916560}
58926561
58936562fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
5894 if (f.liveness.isUnused(inst)) return CValue.none;
5895
58966563 const inst_ty = f.air.typeOfIndex(inst);
58976564 const un_op = f.air.instructions.items(.data)[inst].un_op;
6565 if (f.liveness.isUnused(inst)) {
6566 try reap(f, inst, &.{un_op});
6567 return CValue.none;
6568 }
6569
58986570 const operand = try f.resolveInst(un_op);
6571 try reap(f, inst, &.{un_op});
58996572 const operand_ty = f.air.typeOf(un_op);
59006573
5901 const local = try f.allocLocal(inst_ty, .Const);
59026574 const writer = f.object.writer();
6575 const local = try f.allocLocal(inst, inst_ty);
6576 try f.writeCValue(writer, local, .Other);
59036577 try writer.writeAll(" = zig_neg_");
59046578 try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
59056579 try writer.writeByte('(');
......@@ -5909,12 +6583,17 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue {
59096583}
59106584
59116585fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
5912 if (f.liveness.isUnused(inst)) return CValue.none;
59136586 const un_op = f.air.instructions.items(.data)[inst].un_op;
6587 if (f.liveness.isUnused(inst)) {
6588 try reap(f, inst, &.{un_op});
6589 return CValue.none;
6590 }
6591 const operand = try f.resolveInst(un_op);
6592 try reap(f, inst, &.{un_op});
59146593 const writer = f.object.writer();
59156594 const inst_ty = f.air.typeOfIndex(inst);
5916 const operand = try f.resolveInst(un_op);
5917 const local = try f.allocLocal(inst_ty, .Const);
6595 const local = try f.allocLocal(inst, inst_ty);
6596 try f.writeCValue(writer, local, .Other);
59186597 try writer.writeAll(" = zig_libc_name_");
59196598 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);
59206599 try writer.writeByte('(');
......@@ -5925,18 +6604,21 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal
59256604 return local;
59266605}
59276606
5928fn airBinFloatOp(
5929 f: *Function,
5930 inst: Air.Inst.Index,
5931 operation: []const u8,
5932) !void {
6607fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue {
59336608 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
5934 const writer = f.object.writer();
5935 const inst_ty = f.air.typeOfIndex(inst);
6609 if (f.liveness.isUnused(inst)) {
6610 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6611 return CValue.none;
6612 }
59366613 const lhs = try f.resolveInst(bin_op.lhs);
59376614 const rhs = try f.resolveInst(bin_op.rhs);
6615 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
59386616
5939 try writer.writeAll("zig_libc_name_");
6617 const writer = f.object.writer();
6618 const inst_ty = f.air.typeOfIndex(inst);
6619 const local = try f.allocLocal(inst, inst_ty);
6620 try f.writeCValue(writer, local, .Other);
6621 try writer.writeAll(" = zig_libc_name_");
59406622 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);
59416623 try writer.writeByte('(');
59426624 try writer.writeAll(operation);
......@@ -5944,19 +6626,25 @@ fn airBinFloatOp(
59446626 try f.writeCValue(writer, lhs, .FunctionArgument);
59456627 try writer.writeAll(", ");
59466628 try f.writeCValue(writer, rhs, .FunctionArgument);
5947 try writer.writeAll(")");
6629 try writer.writeAll(");\n");
6630 return local;
59486631}
59496632
59506633fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
5951 if (f.liveness.isUnused(inst)) return CValue.none;
59526634 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
5953 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
6635 const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data;
6636 if (f.liveness.isUnused(inst)) {
6637 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
6638 return CValue.none;
6639 }
59546640 const inst_ty = f.air.typeOfIndex(inst);
5955 const mulend1 = try f.resolveInst(extra.lhs);
5956 const mulend2 = try f.resolveInst(extra.rhs);
6641 const mulend1 = try f.resolveInst(bin_op.lhs);
6642 const mulend2 = try f.resolveInst(bin_op.rhs);
59576643 const addend = try f.resolveInst(pl_op.operand);
6644 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
59586645 const writer = f.object.writer();
5959 const local = try f.allocLocal(inst_ty, .Const);
6646 const local = try f.allocLocal(inst, inst_ty);
6647 try f.writeCValue(writer, local, .Other);
59606648 try writer.writeAll(" = zig_libc_name_");
59616649 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);
59626650 try writer.writeAll("(fma)(");
......@@ -6335,3 +7023,105 @@ fn loweredArrayInfo(ty: Type, target: std.Target) ?Type.ArrayInfo {
63357023 },
63367024 }
63377025}
7026
7027fn reap(f: *Function, inst: Air.Inst.Index, operands: []const Air.Inst.Ref) !void {
7028 assert(operands.len <= Liveness.bpi - 1);
7029 var tomb_bits = f.liveness.getTombBits(inst);
7030 for (operands) |operand| {
7031 const dies = @truncate(u1, tomb_bits) != 0;
7032 tomb_bits >>= 1;
7033 if (!dies) continue;
7034 try die(f, inst, operand);
7035 }
7036}
7037
7038fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void {
7039 const ref_inst = Air.refToIndex(ref) orelse return;
7040 if (f.air.instructions.items(.tag)[ref_inst] == .constant) return;
7041 const c_value = (f.value_map.fetchRemove(ref) orelse return).value;
7042 const local_index = switch (c_value) {
7043 .local => |l| l,
7044 else => return,
7045 };
7046 try freeLocal(f, inst, local_index, ref_inst);
7047}
7048
7049fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_inst: Air.Inst.Index) !void {
7050 const gpa = f.object.dg.gpa;
7051 const local = &f.locals.items[local_index];
7052 log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst });
7053 if (local.loop_depth < f.free_locals_clone_depth) return;
7054 const gop = try f.free_locals_stack.items[local.loop_depth].getOrPutContext(
7055 gpa,
7056 local.ty,
7057 f.tyHashCtx(),
7058 );
7059 if (!gop.found_existing) gop.value_ptr.* = .{};
7060 if (std.debug.runtime_safety) {
7061 // If this trips, it means a local is being inserted into the
7062 // free_locals map while it already exists in the map, which is not
7063 // allowed.
7064 assert(mem.indexOfScalar(LocalIndex, gop.value_ptr.items, local_index) == null);
7065 // If this trips, an unfreeable allocation was attempted to be freed.
7066 assert(!f.allocs.contains(local_index));
7067 }
7068 try gop.value_ptr.append(gpa, local_index);
7069}
7070
7071const BigTomb = struct {
7072 f: *Function,
7073 inst: Air.Inst.Index,
7074 lbt: Liveness.BigTomb,
7075
7076 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) !void {
7077 const dies = bt.lbt.feed();
7078 if (!dies) return;
7079 try die(bt.f, bt.inst, op_ref);
7080 }
7081};
7082
7083fn iterateBigTomb(f: *Function, inst: Air.Inst.Index) BigTomb {
7084 return .{
7085 .f = f,
7086 .inst = inst,
7087 .lbt = f.liveness.iterateBigTomb(inst),
7088 };
7089}
7090
7091/// A naive clone of this map would create copies of the ArrayList which is
7092/// stored in the values. This function additionally clones the values.
7093fn cloneFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) !LocalsMap {
7094 var cloned = try map.clone(gpa);
7095 const values = cloned.values();
7096 var i: usize = 0;
7097 errdefer {
7098 cloned.deinit(gpa);
7099 while (i > 0) {
7100 i -= 1;
7101 values[i].deinit(gpa);
7102 }
7103 }
7104 while (i < values.len) : (i += 1) {
7105 values[i] = try values[i].clone(gpa);
7106 }
7107 return cloned;
7108}
7109
7110fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void {
7111 for (map.values()) |*value| {
7112 value.deinit(gpa);
7113 }
7114 map.deinit(gpa);
7115}
7116
7117fn noticeBranchFrees(f: *Function, pre_locals_len: LocalIndex, inst: Air.Inst.Index) !void {
7118 for (f.locals.items[pre_locals_len..]) |*local, local_offset| {
7119 const local_index = pre_locals_len + @intCast(LocalIndex, local_offset);
7120 if (f.allocs.contains(local_index)) continue; // allocs are not freeable
7121
7122 // free more deeply nested locals from other branches at current depth
7123 assert(local.loop_depth >= f.free_locals_stack.items.len - 1);
7124 local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1);
7125 try freeLocal(f, inst, local_index, 0);
7126 }
7127}
src/codegen/llvm.zig+8-2
......@@ -5346,7 +5346,8 @@ pub const FuncGen = struct {
53465346 const err_union_ty = self.air.typeOf(pl_op.operand);
53475347 const payload_ty = self.air.typeOfIndex(inst);
53485348 const can_elide_load = if (isByRef(payload_ty)) self.canElideLoad(body_tail) else false;
5349 return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, payload_ty);
5349 const is_unused = self.liveness.isUnused(inst);
5350 return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, is_unused, payload_ty);
53505351 }
53515352
53525353 fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
......@@ -5356,7 +5357,8 @@ pub const FuncGen = struct {
53565357 const body = self.air.extra[extra.end..][0..extra.data.body_len];
53575358 const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
53585359 const payload_ty = self.air.typeOfIndex(inst);
5359 return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, payload_ty);
5360 const is_unused = self.liveness.isUnused(inst);
5361 return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, payload_ty);
53605362 }
53615363
53625364 fn lowerTry(
......@@ -5366,6 +5368,7 @@ pub const FuncGen = struct {
53665368 err_union_ty: Type,
53675369 operand_is_ptr: bool,
53685370 can_elide_load: bool,
5371 is_unused: bool,
53695372 result_ty: Type,
53705373 ) !?*llvm.Value {
53715374 const payload_ty = err_union_ty.errorUnionPayload();
......@@ -5405,6 +5408,9 @@ pub const FuncGen = struct {
54055408
54065409 fg.builder.positionBuilderAtEnd(continue_block);
54075410 }
5411 if (is_unused) {
5412 return null;
5413 }
54085414 if (!payload_has_bits) {
54095415 if (!operand_is_ptr) return null;
54105416
src/link/C.zig+2-10
......@@ -133,19 +133,11 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
133133 .code = code.toManaged(module.gpa),
134134 .indent_writer = undefined, // set later so we can get a pointer to object.code
135135 },
136 .arena = std.heap.ArenaAllocator.init(module.gpa),
136137 };
137138
138139 function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
139 defer {
140 function.blocks.deinit(module.gpa);
141 function.value_map.deinit();
142 function.object.code.deinit();
143 for (function.object.dg.typedefs.values()) |typedef| {
144 module.gpa.free(typedef.rendered);
145 }
146 function.object.dg.typedefs.deinit();
147 function.object.dg.fwd_decl.deinit();
148 }
140 defer function.deinit(module.gpa);
149141
150142 codegen.genFunc(&function) catch |err| switch (err) {
151143 error.AnalysisFail => {
test.sh created+20
......@@ -0,0 +1,20 @@
1#!/bin/bash
2if [[ $1 == --enable-fixed-behavior ]]; then
3 declare -A offsets
4 git g -n stage2_c test/behavior | while read -r match; do
5 printf '\e[36mTrying to enable... %s\e[m\n' "$match"
6 file=`cut -d: -f1 <<<"$match"`
7 offset=${offsets[$file]:=0}
8 let line=`cut -d: -f2 <<<"$match"`-$offset
9 contents=`cut -d: -f3- <<<"$match"`
10 sed --in-place "${line}d" "$file"
11 if zigd test -Itest test/behavior.zig -fno-stage1 -fno-LLVM -ofmt=c; then
12 printf '\e[32mTest was enabled! :)\e[m\n'
13 let offsets[$file]+=1
14 else
15 printf '\e[31mTest kept disabled. :(\e[m\n'
16 sed --in-place "${line}i\\
17$contents" "$file"
18 fi
19 done
20fi
test/behavior/math.zig+1
......@@ -1522,6 +1522,7 @@ test "vector integer addition" {
15221522 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15231523 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
15241524 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1525 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
15251526
15261527 const S = struct {
15271528 fn doTheTest() !void {
test/behavior/vector.zig+3
......@@ -78,6 +78,7 @@ test "vector int operators" {
7878 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7979 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
8080 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
81 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
8182
8283 const S = struct {
8384 fn doTheTest() !void {
......@@ -177,6 +178,7 @@ test "tuple to vector" {
177178 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
178179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
179180 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
181 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
180182
181183 if ((builtin.zig_backend == .stage1 or builtin.zig_backend == .stage2_llvm) and
182184 builtin.cpu.arch == .aarch64)
......@@ -943,6 +945,7 @@ test "multiplication-assignment operator with an array operand" {
943945 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
944946 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
945947 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
948 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
946949
947950 const S = struct {
948951 fn doTheTest() !void {