blob 7da99de5 (307063B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const assert = std.debug.assert; 4 const mem = std.mem; 5 const log = std.log.scoped(.c); 6 7 const link = @import("../link.zig"); 8 const Module = @import("../Module.zig"); 9 const Compilation = @import("../Compilation.zig"); 10 const Value = @import("../value.zig").Value; 11 const Type = @import("../type.zig").Type; 12 const TypedValue = @import("../TypedValue.zig"); 13 const C = link.File.C; 14 const Decl = Module.Decl; 15 const trace = @import("../tracy.zig").trace; 16 const LazySrcLoc = Module.LazySrcLoc; 17 const Air = @import("../Air.zig"); 18 const Liveness = @import("../Liveness.zig"); 19 20 const BigIntLimb = std.math.big.Limb; 21 const BigInt = std.math.big.int; 22 23 pub const CType = @import("c/type.zig").CType; 24 25 pub const CValue = union(enum) { 26 none: void, 27 new_local: LocalIndex, 28 local: LocalIndex, 29 /// Address of a local. 30 local_ref: LocalIndex, 31 /// A constant instruction, to be rendered inline. 32 constant: Air.Inst.Ref, 33 /// Index into the parameters 34 arg: usize, 35 /// The array field of a parameter 36 arg_array: usize, 37 /// Index into a tuple's fields 38 field: usize, 39 /// By-value 40 decl: Decl.Index, 41 decl_ref: Decl.Index, 42 /// An undefined value (cannot be dereferenced) 43 undef: Type, 44 /// Render the slice as an identifier (using fmtIdent) 45 identifier: []const u8, 46 /// Render the slice as an payload.identifier (using fmtIdent) 47 payload_identifier: []const u8, 48 }; 49 50 const BlockData = struct { 51 block_id: usize, 52 result: CValue, 53 }; 54 55 pub const CValueMap = std.AutoHashMap(Air.Inst.Index, CValue); 56 57 pub const LazyFnKey = union(enum) { 58 tag_name: Decl.Index, 59 never_tail: Decl.Index, 60 never_inline: Decl.Index, 61 }; 62 pub const LazyFnValue = struct { 63 fn_name: []const u8, 64 data: Data, 65 66 pub const Data = union { 67 tag_name: Type, 68 never_tail: void, 69 never_inline: void, 70 }; 71 }; 72 pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue); 73 74 const LoopDepth = u16; 75 const Local = struct { 76 cty_idx: CType.Index, 77 alignas: CType.AlignAs, 78 79 pub fn getType(local: Local) LocalType { 80 return .{ .cty_idx = local.cty_idx, .alignas = local.alignas }; 81 } 82 }; 83 84 const LocalIndex = u16; 85 const LocalType = struct { cty_idx: CType.Index, alignas: CType.AlignAs }; 86 const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void); 87 const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList); 88 89 const ValueRenderLocation = enum { 90 FunctionArgument, 91 Initializer, 92 StaticInitializer, 93 Other, 94 95 pub fn isInitializer(self: ValueRenderLocation) bool { 96 return switch (self) { 97 .Initializer, .StaticInitializer => true, 98 else => false, 99 }; 100 } 101 }; 102 103 const BuiltinInfo = enum { none, bits }; 104 105 const reserved_idents = std.ComptimeStringMap(void, .{ 106 // C language 107 .{ "alignas", { 108 @setEvalBranchQuota(4000); 109 } }, 110 .{ "alignof", {} }, 111 .{ "asm", {} }, 112 .{ "atomic_bool", {} }, 113 .{ "atomic_char", {} }, 114 .{ "atomic_char16_t", {} }, 115 .{ "atomic_char32_t", {} }, 116 .{ "atomic_int", {} }, 117 .{ "atomic_int_fast16_t", {} }, 118 .{ "atomic_int_fast32_t", {} }, 119 .{ "atomic_int_fast64_t", {} }, 120 .{ "atomic_int_fast8_t", {} }, 121 .{ "atomic_int_least16_t", {} }, 122 .{ "atomic_int_least32_t", {} }, 123 .{ "atomic_int_least64_t", {} }, 124 .{ "atomic_int_least8_t", {} }, 125 .{ "atomic_intmax_t", {} }, 126 .{ "atomic_intptr_t", {} }, 127 .{ "atomic_llong", {} }, 128 .{ "atomic_long", {} }, 129 .{ "atomic_ptrdiff_t", {} }, 130 .{ "atomic_schar", {} }, 131 .{ "atomic_short", {} }, 132 .{ "atomic_size_t", {} }, 133 .{ "atomic_uchar", {} }, 134 .{ "atomic_uint", {} }, 135 .{ "atomic_uint_fast16_t", {} }, 136 .{ "atomic_uint_fast32_t", {} }, 137 .{ "atomic_uint_fast64_t", {} }, 138 .{ "atomic_uint_fast8_t", {} }, 139 .{ "atomic_uint_least16_t", {} }, 140 .{ "atomic_uint_least32_t", {} }, 141 .{ "atomic_uint_least64_t", {} }, 142 .{ "atomic_uint_least8_t", {} }, 143 .{ "atomic_uintmax_t", {} }, 144 .{ "atomic_uintptr_t", {} }, 145 .{ "atomic_ullong", {} }, 146 .{ "atomic_ulong", {} }, 147 .{ "atomic_ushort", {} }, 148 .{ "atomic_wchar_t", {} }, 149 .{ "auto", {} }, 150 .{ "bool", {} }, 151 .{ "break", {} }, 152 .{ "case", {} }, 153 .{ "char", {} }, 154 .{ "complex", {} }, 155 .{ "const", {} }, 156 .{ "continue", {} }, 157 .{ "default", {} }, 158 .{ "do", {} }, 159 .{ "double", {} }, 160 .{ "else", {} }, 161 .{ "enum", {} }, 162 .{ "extern", {} }, 163 .{ "float", {} }, 164 .{ "for", {} }, 165 .{ "fortran", {} }, 166 .{ "goto", {} }, 167 .{ "if", {} }, 168 .{ "imaginary", {} }, 169 .{ "inline", {} }, 170 .{ "int", {} }, 171 .{ "int16_t", {} }, 172 .{ "int32_t", {} }, 173 .{ "int64_t", {} }, 174 .{ "int8_t", {} }, 175 .{ "intptr_t", {} }, 176 .{ "long", {} }, 177 .{ "noreturn", {} }, 178 .{ "register", {} }, 179 .{ "restrict", {} }, 180 .{ "return", {} }, 181 .{ "short", {} }, 182 .{ "signed", {} }, 183 .{ "size_t", {} }, 184 .{ "sizeof", {} }, 185 .{ "ssize_t", {} }, 186 .{ "static", {} }, 187 .{ "static_assert", {} }, 188 .{ "struct", {} }, 189 .{ "switch", {} }, 190 .{ "thread_local", {} }, 191 .{ "typedef", {} }, 192 .{ "uint16_t", {} }, 193 .{ "uint32_t", {} }, 194 .{ "uint64_t", {} }, 195 .{ "uint8_t", {} }, 196 .{ "uintptr_t", {} }, 197 .{ "union", {} }, 198 .{ "unsigned", {} }, 199 .{ "void", {} }, 200 .{ "volatile", {} }, 201 .{ "while", {} }, 202 203 // stdarg.h 204 .{ "va_start", {} }, 205 .{ "va_arg", {} }, 206 .{ "va_end", {} }, 207 .{ "va_copy", {} }, 208 209 // stddef.h 210 .{ "offsetof", {} }, 211 212 // windows.h 213 .{ "max", {} }, 214 .{ "min", {} }, 215 }); 216 217 fn isReservedIdent(ident: []const u8) bool { 218 if (ident.len >= 2 and ident[0] == '_') { // C language 219 switch (ident[1]) { 220 'A'...'Z', '_' => return true, 221 else => return false, 222 } 223 } else if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or 224 mem.startsWith(u8, ident, "DUMMYUNIONNAME")) 225 { // windows.h 226 return true; 227 } else return reserved_idents.has(ident); 228 } 229 230 fn formatIdent( 231 ident: []const u8, 232 comptime fmt: []const u8, 233 options: std.fmt.FormatOptions, 234 writer: anytype, 235 ) !void { 236 _ = options; 237 const solo = fmt.len != 0 and fmt[0] == ' '; // space means solo; not part of a bigger ident. 238 if (solo and isReservedIdent(ident)) { 239 try writer.writeAll("zig_e_"); 240 } 241 for (ident, 0..) |c, i| { 242 switch (c) { 243 'a'...'z', 'A'...'Z', '_' => try writer.writeByte(c), 244 '.' => try writer.writeByte('_'), 245 '0'...'9' => if (i == 0) { 246 try writer.print("_{x:2}", .{c}); 247 } else { 248 try writer.writeByte(c); 249 }, 250 else => try writer.print("_{x:2}", .{c}), 251 } 252 } 253 } 254 255 pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { 256 return .{ .data = ident }; 257 } 258 259 /// This data is available when outputting .c code for a `*Module.Fn`. 260 /// It is not available when generating .h file. 261 pub const Function = struct { 262 air: Air, 263 liveness: Liveness, 264 value_map: CValueMap, 265 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, 266 next_arg_index: usize = 0, 267 next_block_index: usize = 0, 268 object: Object, 269 lazy_fns: LazyFnMap, 270 func: *Module.Fn, 271 /// All the locals, to be emitted at the top of the function. 272 locals: std.ArrayListUnmanaged(Local) = .{}, 273 /// Which locals are available for reuse, based on Type. 274 free_locals_map: LocalsMap = .{}, 275 /// Locals which will not be freed by Liveness. This is used after a 276 /// Function body is lowered in order to make `free_locals_map` have 277 /// 100% of the locals within so that it can be used to render the block 278 /// of variable declarations at the top of a function, sorted descending 279 /// by type alignment. 280 /// The value is whether the alloc needs to be emitted in the header. 281 allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .{}, 282 283 fn resolveInst(f: *Function, ref: Air.Inst.Ref) !CValue { 284 if (Air.refToIndex(ref)) |inst| { 285 const gop = try f.value_map.getOrPut(inst); 286 if (gop.found_existing) return gop.value_ptr.*; 287 288 const val = f.air.value(ref).?; 289 const ty = f.air.typeOf(ref); 290 291 const result: CValue = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { 292 const writer = f.object.code_header.writer(); 293 const alignment = 0; 294 const decl_c_value = try f.allocLocalValue(ty, alignment); 295 const gpa = f.object.dg.gpa; 296 try f.allocs.put(gpa, decl_c_value.new_local, false); 297 try writer.writeAll("static "); 298 try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, alignment, .complete); 299 try writer.writeAll(" = "); 300 try f.object.dg.renderValue(writer, ty, val, .StaticInitializer); 301 try writer.writeAll(";\n "); 302 break :result decl_c_value; 303 } else .{ .constant = ref }; 304 305 gop.value_ptr.* = result; 306 return result; 307 } else return .{ .constant = ref }; 308 } 309 310 fn wantSafety(f: *Function) bool { 311 return switch (f.object.dg.module.optimizeMode()) { 312 .Debug, .ReleaseSafe => true, 313 .ReleaseFast, .ReleaseSmall => false, 314 }; 315 } 316 317 /// Skips the reuse logic. This function should be used for any persistent allocation, i.e. 318 /// those which go into `allocs`. This function does not add the resulting local into `allocs`; 319 /// that responsibility lies with the caller. 320 fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue { 321 const gpa = f.object.dg.gpa; 322 const target = f.object.dg.module.getTarget(); 323 try f.locals.append(gpa, .{ 324 .cty_idx = try f.typeToIndex(ty, .complete), 325 .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)), 326 }); 327 return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; 328 } 329 330 fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { 331 const result = try f.allocAlignedLocal(ty, .{}, 0); 332 log.debug("%{d}: allocating t{d}", .{ inst, result.new_local }); 333 return result; 334 } 335 336 /// Only allocates the local; does not print anything. Will attempt to re-use locals, so should 337 /// not be used for persistent locals (i.e. those in `allocs`). 338 fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue { 339 const target = f.object.dg.module.getTarget(); 340 if (f.free_locals_map.getPtr(.{ 341 .cty_idx = try f.typeToIndex(ty, .complete), 342 .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)), 343 })) |locals_list| { 344 if (locals_list.popOrNull()) |local_entry| { 345 return .{ .new_local = local_entry.key }; 346 } 347 } 348 349 return try f.allocLocalValue(ty, alignment); 350 } 351 352 fn writeCValue(f: *Function, w: anytype, c_value: CValue, location: ValueRenderLocation) !void { 353 switch (c_value) { 354 .constant => |inst| { 355 const ty = f.air.typeOf(inst); 356 const val = f.air.value(inst).?; 357 return f.object.dg.renderValue(w, ty, val, location); 358 }, 359 .undef => |ty| return f.object.dg.renderValue(w, ty, Value.undef, location), 360 else => return f.object.dg.writeCValue(w, c_value), 361 } 362 } 363 364 fn writeCValueDeref(f: *Function, w: anytype, c_value: CValue) !void { 365 switch (c_value) { 366 .constant => |inst| { 367 const ty = f.air.typeOf(inst); 368 const val = f.air.value(inst).?; 369 try w.writeAll("(*"); 370 try f.object.dg.renderValue(w, ty, val, .Other); 371 return w.writeByte(')'); 372 }, 373 else => return f.object.dg.writeCValueDeref(w, c_value), 374 } 375 } 376 377 fn writeCValueMember(f: *Function, w: anytype, c_value: CValue, member: CValue) !void { 378 switch (c_value) { 379 .constant => |inst| { 380 const ty = f.air.typeOf(inst); 381 const val = f.air.value(inst).?; 382 try f.object.dg.renderValue(w, ty, val, .Other); 383 try w.writeByte('.'); 384 return f.writeCValue(w, member, .Other); 385 }, 386 else => return f.object.dg.writeCValueMember(w, c_value, member), 387 } 388 } 389 390 fn writeCValueDerefMember(f: *Function, w: anytype, c_value: CValue, member: CValue) !void { 391 switch (c_value) { 392 .constant => |inst| { 393 const ty = f.air.typeOf(inst); 394 const val = f.air.value(inst).?; 395 try w.writeByte('('); 396 try f.object.dg.renderValue(w, ty, val, .Other); 397 try w.writeAll(")->"); 398 return f.writeCValue(w, member, .Other); 399 }, 400 else => return f.object.dg.writeCValueDerefMember(w, c_value, member), 401 } 402 } 403 404 fn fail(f: *Function, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { 405 return f.object.dg.fail(format, args); 406 } 407 408 fn indexToCType(f: *Function, idx: CType.Index) CType { 409 return f.object.dg.indexToCType(idx); 410 } 411 412 fn typeToIndex(f: *Function, ty: Type, kind: CType.Kind) !CType.Index { 413 return f.object.dg.typeToIndex(ty, kind); 414 } 415 416 fn typeToCType(f: *Function, ty: Type, kind: CType.Kind) !CType { 417 return f.object.dg.typeToCType(ty, kind); 418 } 419 420 fn byteSize(f: *Function, cty: CType) u64 { 421 return f.object.dg.byteSize(cty); 422 } 423 424 fn renderType(f: *Function, w: anytype, t: Type) !void { 425 return f.object.dg.renderType(w, t); 426 } 427 428 fn renderCType(f: *Function, w: anytype, t: CType.Index) !void { 429 return f.object.dg.renderCType(w, t); 430 } 431 432 fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, v: Vectorize, src_ty: Type, location: ValueRenderLocation) !void { 433 return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location); 434 } 435 436 fn fmtIntLiteral(f: *Function, ty: Type, val: Value) !std.fmt.Formatter(formatIntLiteral) { 437 return f.object.dg.fmtIntLiteral(ty, val, .Other); 438 } 439 440 fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 { 441 const gpa = f.object.dg.gpa; 442 const gop = try f.lazy_fns.getOrPut(gpa, key); 443 if (!gop.found_existing) { 444 errdefer _ = f.lazy_fns.pop(); 445 446 var promoted = f.object.dg.ctypes.promote(gpa); 447 defer f.object.dg.ctypes.demote(promoted); 448 const arena = promoted.arena.allocator(); 449 450 gop.value_ptr.* = .{ 451 .fn_name = switch (key) { 452 .tag_name, 453 .never_tail, 454 .never_inline, 455 => |owner_decl| try std.fmt.allocPrint(arena, "zig_{s}_{}__{d}", .{ 456 @tagName(key), 457 fmtIdent(mem.span(f.object.dg.module.declPtr(owner_decl).name)), 458 @enumToInt(owner_decl), 459 }), 460 }, 461 .data = switch (key) { 462 .tag_name => .{ .tag_name = try data.tag_name.copy(arena) }, 463 .never_tail => .{ .never_tail = data.never_tail }, 464 .never_inline => .{ .never_inline = data.never_inline }, 465 }, 466 }; 467 } 468 return gop.value_ptr.fn_name; 469 } 470 471 pub fn deinit(f: *Function) void { 472 const gpa = f.object.dg.gpa; 473 f.allocs.deinit(gpa); 474 f.locals.deinit(gpa); 475 deinitFreeLocalsMap(gpa, &f.free_locals_map); 476 f.blocks.deinit(gpa); 477 f.value_map.deinit(); 478 f.lazy_fns.deinit(gpa); 479 f.object.code.deinit(); 480 f.object.dg.ctypes.deinit(gpa); 481 f.object.dg.fwd_decl.deinit(); 482 } 483 }; 484 485 /// This data is available when outputting .c code for a `Module`. 486 /// It is not available when generating .h file. 487 pub const Object = struct { 488 dg: DeclGen, 489 code: std.ArrayList(u8), 490 /// Goes before code. Initialized and deinitialized in `genFunc`. 491 code_header: std.ArrayList(u8) = undefined, 492 indent_writer: IndentWriter(std.ArrayList(u8).Writer), 493 494 fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer { 495 return o.indent_writer.writer(); 496 } 497 }; 498 499 /// This data is available both when outputting .c code and when outputting an .h file. 500 pub const DeclGen = struct { 501 gpa: mem.Allocator, 502 module: *Module, 503 decl: ?*Decl, 504 decl_index: Decl.OptionalIndex, 505 fwd_decl: std.ArrayList(u8), 506 error_msg: ?*Module.ErrorMsg, 507 ctypes: CType.Store, 508 509 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { 510 @setCold(true); 511 const src = LazySrcLoc.nodeOffset(0); 512 const src_loc = src.toSrcLoc(dg.decl.?); 513 dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args); 514 return error.AnalysisFail; 515 } 516 517 fn renderDeclValue( 518 dg: *DeclGen, 519 writer: anytype, 520 ty: Type, 521 val: Value, 522 decl_index: Decl.Index, 523 location: ValueRenderLocation, 524 ) error{ OutOfMemory, AnalysisFail }!void { 525 const decl = dg.module.declPtr(decl_index); 526 assert(decl.has_tv); 527 528 // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. 529 if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) { 530 return dg.writeCValue(writer, .{ .undef = ty }); 531 } 532 533 // Chase function values in order to be able to reference the original function. 534 inline for (.{ .function, .extern_fn }) |tag| 535 if (decl.val.castTag(tag)) |func| 536 if (func.data.owner_decl != decl_index) 537 return dg.renderDeclValue(writer, ty, val, func.data.owner_decl, location); 538 539 if (decl.val.castTag(.variable)) |var_payload| 540 try dg.renderFwdDecl(decl_index, var_payload.data); 541 542 if (ty.isSlice()) { 543 if (location == .StaticInitializer) { 544 try writer.writeByte('{'); 545 } else { 546 try writer.writeByte('('); 547 try dg.renderType(writer, ty); 548 try writer.writeAll("){ .ptr = "); 549 } 550 551 var buf: Type.SlicePtrFieldTypeBuffer = undefined; 552 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr(), .Initializer); 553 554 var len_pl: Value.Payload.U64 = .{ 555 .base = .{ .tag = .int_u64 }, 556 .data = val.sliceLen(dg.module), 557 }; 558 const len_val = Value.initPayload(&len_pl.base); 559 560 if (location == .StaticInitializer) { 561 return writer.print(", {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)}); 562 } else { 563 return writer.print(", .len = {} }}", .{try dg.fmtIntLiteral(Type.usize, len_val, .Other)}); 564 } 565 } 566 567 // We shouldn't cast C function pointers as this is UB (when you call 568 // them). The analysis until now should ensure that the C function 569 // pointers are compatible. If they are not, then there is a bug 570 // somewhere and we should let the C compiler tell us about it. 571 const need_typecast = if (ty.castPtrToFn()) |_| false else !ty.eql(decl.ty, dg.module); 572 if (need_typecast) { 573 try writer.writeAll("(("); 574 try dg.renderType(writer, ty); 575 try writer.writeByte(')'); 576 } 577 try writer.writeByte('&'); 578 try dg.renderDeclName(writer, decl_index, 0); 579 if (need_typecast) try writer.writeByte(')'); 580 } 581 582 // Renders a "parent" pointer by recursing to the root decl/variable 583 // that its contents are defined with respect to. 584 // 585 // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr 586 fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type, location: ValueRenderLocation) error{ OutOfMemory, AnalysisFail }!void { 587 if (!ptr_ty.isSlice()) { 588 try writer.writeByte('('); 589 try dg.renderType(writer, ptr_ty); 590 try writer.writeByte(')'); 591 } 592 switch (ptr_val.tag()) { 593 .int_u64, .one => try writer.print("{x}", .{try dg.fmtIntLiteral(Type.usize, ptr_val, .Other)}), 594 .decl_ref_mut, .decl_ref, .variable => { 595 const decl_index = switch (ptr_val.tag()) { 596 .decl_ref => ptr_val.castTag(.decl_ref).?.data, 597 .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index, 598 .variable => ptr_val.castTag(.variable).?.data.owner_decl, 599 else => unreachable, 600 }; 601 try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl_index, location); 602 }, 603 .field_ptr => { 604 const target = dg.module.getTarget(); 605 const field_ptr = ptr_val.castTag(.field_ptr).?.data; 606 607 // Ensure complete type definition is visible before accessing fields. 608 _ = try dg.typeToIndex(field_ptr.container_ty, .complete); 609 610 var container_ptr_pl = ptr_ty.ptrInfo(); 611 container_ptr_pl.data.pointee_type = field_ptr.container_ty; 612 const container_ptr_ty = Type.initPayload(&container_ptr_pl.base); 613 614 switch (fieldLocation( 615 field_ptr.container_ty, 616 ptr_ty, 617 @intCast(u32, field_ptr.field_index), 618 target, 619 )) { 620 .begin => try dg.renderParentPtr( 621 writer, 622 field_ptr.container_ptr, 623 container_ptr_ty, 624 location, 625 ), 626 .field => |field| { 627 try writer.writeAll("&("); 628 try dg.renderParentPtr( 629 writer, 630 field_ptr.container_ptr, 631 container_ptr_ty, 632 location, 633 ); 634 try writer.writeAll(")->"); 635 try dg.writeCValue(writer, field); 636 }, 637 .byte_offset => |byte_offset| { 638 var u8_ptr_pl = ptr_ty.ptrInfo(); 639 u8_ptr_pl.data.pointee_type = Type.u8; 640 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); 641 642 var byte_offset_pl = Value.Payload.U64{ 643 .base = .{ .tag = .int_u64 }, 644 .data = byte_offset, 645 }; 646 const byte_offset_val = Value.initPayload(&byte_offset_pl.base); 647 648 try writer.writeAll("(("); 649 try dg.renderType(writer, u8_ptr_ty); 650 try writer.writeByte(')'); 651 try dg.renderParentPtr( 652 writer, 653 field_ptr.container_ptr, 654 container_ptr_ty, 655 location, 656 ); 657 try writer.print(" + {})", .{ 658 try dg.fmtIntLiteral(Type.usize, byte_offset_val, .Other), 659 }); 660 }, 661 .end => { 662 try writer.writeAll("(("); 663 try dg.renderParentPtr( 664 writer, 665 field_ptr.container_ptr, 666 container_ptr_ty, 667 location, 668 ); 669 try writer.print(") + {})", .{ 670 try dg.fmtIntLiteral(Type.usize, Value.one, .Other), 671 }); 672 }, 673 } 674 }, 675 .elem_ptr => { 676 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; 677 var elem_ptr_ty_pl: Type.Payload.ElemType = .{ 678 .base = .{ .tag = .c_mut_pointer }, 679 .data = elem_ptr.elem_ty, 680 }; 681 const elem_ptr_ty = Type.initPayload(&elem_ptr_ty_pl.base); 682 683 try writer.writeAll("&("); 684 try dg.renderParentPtr(writer, elem_ptr.array_ptr, elem_ptr_ty, location); 685 try writer.print(")[{d}]", .{elem_ptr.index}); 686 }, 687 .opt_payload_ptr, .eu_payload_ptr => { 688 const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data; 689 var container_ptr_ty_pl: Type.Payload.ElemType = .{ 690 .base = .{ .tag = .c_mut_pointer }, 691 .data = payload_ptr.container_ty, 692 }; 693 const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); 694 695 // Ensure complete type definition is visible before accessing fields. 696 _ = try dg.typeToIndex(payload_ptr.container_ty, .complete); 697 698 try writer.writeAll("&("); 699 try dg.renderParentPtr(writer, payload_ptr.container_ptr, container_ptr_ty, location); 700 try writer.writeAll(")->payload"); 701 }, 702 else => unreachable, 703 } 704 } 705 706 fn renderValue( 707 dg: *DeclGen, 708 writer: anytype, 709 ty: Type, 710 arg_val: Value, 711 location: ValueRenderLocation, 712 ) error{ OutOfMemory, AnalysisFail }!void { 713 var val = arg_val; 714 if (val.castTag(.runtime_value)) |rt| { 715 val = rt.data; 716 } 717 const target = dg.module.getTarget(); 718 const initializer_type: ValueRenderLocation = switch (location) { 719 .StaticInitializer => .StaticInitializer, 720 else => .Initializer, 721 }; 722 723 const safety_on = switch (dg.module.optimizeMode()) { 724 .Debug, .ReleaseSafe => true, 725 .ReleaseFast, .ReleaseSmall => false, 726 }; 727 728 if (val.isUndefDeep()) { 729 switch (ty.zigTypeTag()) { 730 .Bool => { 731 if (safety_on) { 732 return writer.writeAll("0xaa"); 733 } else { 734 return writer.writeAll("false"); 735 } 736 }, 737 .Int, .Enum, .ErrorSet => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, val, location)}), 738 .Float => { 739 const bits = ty.floatBits(target); 740 var repr_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = bits }; 741 const repr_ty = Type.initPayload(&repr_pl.base); 742 743 try writer.writeAll("zig_cast_"); 744 try dg.renderTypeForBuiltinFnName(writer, ty); 745 try writer.writeAll(" zig_make_"); 746 try dg.renderTypeForBuiltinFnName(writer, ty); 747 try writer.writeByte('('); 748 switch (bits) { 749 16 => try writer.print("{x}", .{@bitCast(f16, undefPattern(i16))}), 750 32 => try writer.print("{x}", .{@bitCast(f32, undefPattern(i32))}), 751 64 => try writer.print("{x}", .{@bitCast(f64, undefPattern(i64))}), 752 80 => try writer.print("{x}", .{@bitCast(f80, undefPattern(i80))}), 753 128 => try writer.print("{x}", .{@bitCast(f128, undefPattern(i128))}), 754 else => unreachable, 755 } 756 try writer.writeAll(", "); 757 try dg.renderValue(writer, repr_ty, Value.undef, .FunctionArgument); 758 return writer.writeByte(')'); 759 }, 760 .Pointer => if (ty.isSlice()) { 761 if (!location.isInitializer()) { 762 try writer.writeByte('('); 763 try dg.renderType(writer, ty); 764 try writer.writeByte(')'); 765 } 766 767 try writer.writeAll("{("); 768 var buf: Type.SlicePtrFieldTypeBuffer = undefined; 769 const ptr_ty = ty.slicePtrFieldType(&buf); 770 try dg.renderType(writer, ptr_ty); 771 return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); 772 } else { 773 try writer.writeAll("(("); 774 try dg.renderType(writer, ty); 775 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); 776 }, 777 .Optional => { 778 var opt_buf: Type.Payload.ElemType = undefined; 779 const payload_ty = ty.optionalChild(&opt_buf); 780 781 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 782 return dg.renderValue(writer, Type.bool, val, location); 783 } 784 785 if (ty.optionalReprIsPayload()) { 786 return dg.renderValue(writer, payload_ty, val, location); 787 } 788 789 if (!location.isInitializer()) { 790 try writer.writeByte('('); 791 try dg.renderType(writer, ty); 792 try writer.writeByte(')'); 793 } 794 795 try writer.writeAll("{ .payload = "); 796 try dg.renderValue(writer, payload_ty, val, initializer_type); 797 try writer.writeAll(", .is_null = "); 798 try dg.renderValue(writer, Type.bool, val, initializer_type); 799 return writer.writeAll(" }"); 800 }, 801 .Struct => switch (ty.containerLayout()) { 802 .Auto, .Extern => { 803 if (!location.isInitializer()) { 804 try writer.writeByte('('); 805 try dg.renderType(writer, ty); 806 try writer.writeByte(')'); 807 } 808 809 try writer.writeByte('{'); 810 var empty = true; 811 for (0..ty.structFieldCount()) |field_i| { 812 if (ty.structFieldIsComptime(field_i)) continue; 813 const field_ty = ty.structFieldType(field_i); 814 if (!field_ty.hasRuntimeBits()) continue; 815 816 if (!empty) try writer.writeByte(','); 817 try dg.renderValue(writer, field_ty, val, initializer_type); 818 819 empty = false; 820 } 821 822 return writer.writeByte('}'); 823 }, 824 .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef, .Other)}), 825 }, 826 .Union => { 827 if (!location.isInitializer()) { 828 try writer.writeByte('('); 829 try dg.renderType(writer, ty); 830 try writer.writeByte(')'); 831 } 832 833 try writer.writeByte('{'); 834 if (ty.unionTagTypeSafety()) |tag_ty| { 835 const layout = ty.unionGetLayout(target); 836 if (layout.tag_size != 0) { 837 try writer.writeAll(" .tag = "); 838 try dg.renderValue(writer, tag_ty, val, initializer_type); 839 } 840 if (ty.unionHasAllZeroBitFieldTypes()) return try writer.writeByte('}'); 841 if (layout.tag_size != 0) try writer.writeByte(','); 842 try writer.writeAll(" .payload = {"); 843 } 844 for (ty.unionFields().values()) |field| { 845 if (!field.ty.hasRuntimeBits()) continue; 846 try dg.renderValue(writer, field.ty, val, initializer_type); 847 break; 848 } 849 if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); 850 return writer.writeByte('}'); 851 }, 852 .ErrorUnion => { 853 const payload_ty = ty.errorUnionPayload(); 854 const error_ty = ty.errorUnionSet(); 855 856 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 857 return dg.renderValue(writer, error_ty, val, location); 858 } 859 860 if (!location.isInitializer()) { 861 try writer.writeByte('('); 862 try dg.renderType(writer, ty); 863 try writer.writeByte(')'); 864 } 865 866 try writer.writeAll("{ .payload = "); 867 try dg.renderValue(writer, payload_ty, val, initializer_type); 868 try writer.writeAll(", .error = "); 869 try dg.renderValue(writer, error_ty, val, initializer_type); 870 return writer.writeAll(" }"); 871 }, 872 .Array, .Vector => { 873 const ai = ty.arrayInfo(); 874 if (ai.elem_type.eql(Type.u8, dg.module)) { 875 var literal = stringLiteral(writer); 876 try literal.start(); 877 const c_len = ty.arrayLenIncludingSentinel(); 878 var index: u64 = 0; 879 while (index < c_len) : (index += 1) 880 try literal.writeChar(0xaa); 881 return literal.end(); 882 } else { 883 if (!location.isInitializer()) { 884 try writer.writeByte('('); 885 try dg.renderType(writer, ty); 886 try writer.writeByte(')'); 887 } 888 889 try writer.writeByte('{'); 890 const c_len = ty.arrayLenIncludingSentinel(); 891 var index: u64 = 0; 892 while (index < c_len) : (index += 1) { 893 if (index > 0) try writer.writeAll(", "); 894 try dg.renderValue(writer, ty.childType(), val, initializer_type); 895 } 896 return writer.writeByte('}'); 897 } 898 }, 899 .ComptimeInt, 900 .ComptimeFloat, 901 .Type, 902 .EnumLiteral, 903 .Void, 904 .NoReturn, 905 .Undefined, 906 .Null, 907 .Opaque, 908 => unreachable, 909 910 .Fn, 911 .Frame, 912 .AnyFrame, 913 => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ 914 @tagName(tag), 915 }), 916 } 917 unreachable; 918 } 919 switch (ty.zigTypeTag()) { 920 .Int => switch (val.tag()) { 921 .field_ptr, 922 .elem_ptr, 923 .opt_payload_ptr, 924 .eu_payload_ptr, 925 .decl_ref_mut, 926 .decl_ref, 927 => try dg.renderParentPtr(writer, val, ty, location), 928 else => try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, location)}), 929 }, 930 .Float => { 931 const bits = ty.floatBits(target); 932 const f128_val = val.toFloat(f128); 933 934 var repr_ty_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = bits }; 935 const repr_ty = Type.initPayload(&repr_ty_pl.base); 936 937 assert(bits <= 128); 938 var repr_val_limbs: [BigInt.calcTwosCompLimbCount(128)]BigIntLimb = undefined; 939 var repr_val_big = BigInt.Mutable{ 940 .limbs = &repr_val_limbs, 941 .len = undefined, 942 .positive = undefined, 943 }; 944 945 switch (bits) { 946 16 => repr_val_big.set(@bitCast(u16, val.toFloat(f16))), 947 32 => repr_val_big.set(@bitCast(u32, val.toFloat(f32))), 948 64 => repr_val_big.set(@bitCast(u64, val.toFloat(f64))), 949 80 => repr_val_big.set(@bitCast(u80, val.toFloat(f80))), 950 128 => repr_val_big.set(@bitCast(u128, f128_val)), 951 else => unreachable, 952 } 953 954 var repr_val_pl = Value.Payload.BigInt{ 955 .base = .{ 956 .tag = if (repr_val_big.positive) .int_big_positive else .int_big_negative, 957 }, 958 .data = repr_val_big.limbs[0..repr_val_big.len], 959 }; 960 const repr_val = Value.initPayload(&repr_val_pl.base); 961 962 try writer.writeAll("zig_cast_"); 963 try dg.renderTypeForBuiltinFnName(writer, ty); 964 try writer.writeByte(' '); 965 var empty = true; 966 if (std.math.isFinite(f128_val)) { 967 try writer.writeAll("zig_make_"); 968 try dg.renderTypeForBuiltinFnName(writer, ty); 969 try writer.writeByte('('); 970 switch (bits) { 971 16 => try writer.print("{x}", .{val.toFloat(f16)}), 972 32 => try writer.print("{x}", .{val.toFloat(f32)}), 973 64 => try writer.print("{x}", .{val.toFloat(f64)}), 974 80 => try writer.print("{x}", .{val.toFloat(f80)}), 975 128 => try writer.print("{x}", .{f128_val}), 976 else => unreachable, 977 } 978 try writer.writeAll(", "); 979 empty = false; 980 } else { 981 // isSignalNan is equivalent to isNan currently, and MSVC doens't have nans, so prefer nan 982 const operation = if (std.math.isNan(f128_val)) 983 "nan" 984 else if (std.math.isSignalNan(f128_val)) 985 "nans" 986 else if (std.math.isInf(f128_val)) 987 "inf" 988 else 989 unreachable; 990 991 if (location == .StaticInitializer) { 992 if (!std.math.isNan(f128_val) and std.math.isSignalNan(f128_val)) 993 return dg.fail("TODO: C backend: implement nans rendering in static initializers", .{}); 994 995 // MSVC doesn't have a way to define a custom or signaling NaN value in a constant expression 996 997 // TODO: Re-enable this check, otherwise we're writing qnan bit patterns on msvc incorrectly 998 // if (std.math.isNan(f128_val) and f128_val != std.math.qnan_f128) 999 // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); 1000 } 1001 1002 try writer.writeAll("zig_"); 1003 try writer.writeAll(if (location == .StaticInitializer) "init" else "make"); 1004 try writer.writeAll("_special_"); 1005 try dg.renderTypeForBuiltinFnName(writer, ty); 1006 try writer.writeByte('('); 1007 if (std.math.signbit(f128_val)) try writer.writeByte('-'); 1008 try writer.writeAll(", "); 1009 try writer.writeAll(operation); 1010 try writer.writeAll(", "); 1011 if (std.math.isNan(f128_val)) switch (bits) { 1012 // We only actually need to pass the significand, but it will get 1013 // properly masked anyway, so just pass the whole value. 1014 16 => try writer.print("\"0x{x}\"", .{@bitCast(u16, val.toFloat(f16))}), 1015 32 => try writer.print("\"0x{x}\"", .{@bitCast(u32, val.toFloat(f32))}), 1016 64 => try writer.print("\"0x{x}\"", .{@bitCast(u64, val.toFloat(f64))}), 1017 80 => try writer.print("\"0x{x}\"", .{@bitCast(u80, val.toFloat(f80))}), 1018 128 => try writer.print("\"0x{x}\"", .{@bitCast(u128, f128_val)}), 1019 else => unreachable, 1020 }; 1021 try writer.writeAll(", "); 1022 empty = false; 1023 } 1024 try writer.print("{x}", .{try dg.fmtIntLiteral(repr_ty, repr_val, location)}); 1025 if (!empty) try writer.writeByte(')'); 1026 return; 1027 }, 1028 .Pointer => switch (val.tag()) { 1029 .null_value, .zero => if (ty.isSlice()) { 1030 var slice_pl = Value.Payload.Slice{ 1031 .base = .{ .tag = .slice }, 1032 .data = .{ .ptr = val, .len = Value.undef }, 1033 }; 1034 const slice_val = Value.initPayload(&slice_pl.base); 1035 1036 return dg.renderValue(writer, ty, slice_val, location); 1037 } else { 1038 try writer.writeAll("(("); 1039 try dg.renderType(writer, ty); 1040 try writer.writeAll(")NULL)"); 1041 }, 1042 .variable => { 1043 const decl = val.castTag(.variable).?.data.owner_decl; 1044 return dg.renderDeclValue(writer, ty, val, decl, location); 1045 }, 1046 .slice => { 1047 if (!location.isInitializer()) { 1048 try writer.writeByte('('); 1049 try dg.renderType(writer, ty); 1050 try writer.writeByte(')'); 1051 } 1052 1053 const slice = val.castTag(.slice).?.data; 1054 var buf: Type.SlicePtrFieldTypeBuffer = undefined; 1055 1056 try writer.writeByte('{'); 1057 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr, initializer_type); 1058 try writer.writeAll(", "); 1059 try dg.renderValue(writer, Type.usize, slice.len, initializer_type); 1060 try writer.writeByte('}'); 1061 }, 1062 .function => { 1063 const func = val.castTag(.function).?.data; 1064 try dg.renderDeclName(writer, func.owner_decl, 0); 1065 }, 1066 .extern_fn => { 1067 const extern_fn = val.castTag(.extern_fn).?.data; 1068 try dg.renderDeclName(writer, extern_fn.owner_decl, 0); 1069 }, 1070 .int_u64, .one, .int_big_positive, .lazy_align, .lazy_size => { 1071 try writer.writeAll("(("); 1072 try dg.renderType(writer, ty); 1073 return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val, .Other)}); 1074 }, 1075 .field_ptr, 1076 .elem_ptr, 1077 .opt_payload_ptr, 1078 .eu_payload_ptr, 1079 .decl_ref_mut, 1080 .decl_ref, 1081 => try dg.renderParentPtr(writer, val, ty, location), 1082 else => unreachable, 1083 }, 1084 .Array, .Vector => { 1085 if (location == .FunctionArgument) { 1086 try writer.writeByte('('); 1087 try dg.renderType(writer, ty); 1088 try writer.writeByte(')'); 1089 } 1090 1091 // First try specific tag representations for more efficiency. 1092 switch (val.tag()) { 1093 .undef, .empty_struct_value, .empty_array => { 1094 const ai = ty.arrayInfo(); 1095 try writer.writeByte('{'); 1096 if (ai.sentinel) |s| { 1097 try dg.renderValue(writer, ai.elem_type, s, initializer_type); 1098 } else { 1099 try writer.writeByte('0'); 1100 } 1101 try writer.writeByte('}'); 1102 }, 1103 .bytes, .str_lit => |t| { 1104 const bytes = switch (t) { 1105 .bytes => val.castTag(.bytes).?.data, 1106 .str_lit => bytes: { 1107 const str_lit = val.castTag(.str_lit).?.data; 1108 break :bytes dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; 1109 }, 1110 else => unreachable, 1111 }; 1112 const sentinel = if (ty.sentinel()) |sentinel| @intCast(u8, sentinel.toUnsignedInt(target)) else null; 1113 try writer.print("{s}", .{ 1114 fmtStringLiteral(bytes[0..@intCast(usize, ty.arrayLen())], sentinel), 1115 }); 1116 }, 1117 else => { 1118 // Fall back to generic implementation. 1119 var arena = std.heap.ArenaAllocator.init(dg.gpa); 1120 defer arena.deinit(); 1121 const arena_allocator = arena.allocator(); 1122 1123 // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal 1124 const max_string_initializer_len = 65535; 1125 1126 const ai = ty.arrayInfo(); 1127 if (ai.elem_type.eql(Type.u8, dg.module)) { 1128 if (ai.len <= max_string_initializer_len) { 1129 var literal = stringLiteral(writer); 1130 try literal.start(); 1131 var index: usize = 0; 1132 while (index < ai.len) : (index += 1) { 1133 const elem_val = try val.elemValue(dg.module, arena_allocator, index); 1134 const elem_val_u8 = if (elem_val.isUndef()) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(target)); 1135 try literal.writeChar(elem_val_u8); 1136 } 1137 if (ai.sentinel) |s| { 1138 const s_u8 = @intCast(u8, s.toUnsignedInt(target)); 1139 if (s_u8 != 0) try literal.writeChar(s_u8); 1140 } 1141 try literal.end(); 1142 } else { 1143 try writer.writeByte('{'); 1144 var index: usize = 0; 1145 while (index < ai.len) : (index += 1) { 1146 if (index != 0) try writer.writeByte(','); 1147 const elem_val = try val.elemValue(dg.module, arena_allocator, index); 1148 const elem_val_u8 = if (elem_val.isUndef()) undefPattern(u8) else @intCast(u8, elem_val.toUnsignedInt(target)); 1149 try writer.print("'\\x{x}'", .{elem_val_u8}); 1150 } 1151 if (ai.sentinel) |s| { 1152 if (index != 0) try writer.writeByte(','); 1153 try dg.renderValue(writer, ai.elem_type, s, initializer_type); 1154 } 1155 try writer.writeByte('}'); 1156 } 1157 } else { 1158 try writer.writeByte('{'); 1159 var index: usize = 0; 1160 while (index < ai.len) : (index += 1) { 1161 if (index != 0) try writer.writeByte(','); 1162 const elem_val = try val.elemValue(dg.module, arena_allocator, index); 1163 try dg.renderValue(writer, ai.elem_type, elem_val, initializer_type); 1164 } 1165 if (ai.sentinel) |s| { 1166 if (index != 0) try writer.writeByte(','); 1167 try dg.renderValue(writer, ai.elem_type, s, initializer_type); 1168 } 1169 try writer.writeByte('}'); 1170 } 1171 }, 1172 } 1173 }, 1174 .Bool => { 1175 if (val.toBool()) { 1176 return writer.writeAll("true"); 1177 } else { 1178 return writer.writeAll("false"); 1179 } 1180 }, 1181 .Optional => { 1182 var opt_buf: Type.Payload.ElemType = undefined; 1183 const payload_ty = ty.optionalChild(&opt_buf); 1184 1185 const is_null_val = Value.makeBool(val.tag() == .null_value); 1186 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) 1187 return dg.renderValue(writer, Type.bool, is_null_val, location); 1188 1189 if (ty.optionalReprIsPayload()) { 1190 const payload_val = if (val.castTag(.opt_payload)) |pl| pl.data else val; 1191 return dg.renderValue(writer, payload_ty, payload_val, location); 1192 } 1193 1194 if (!location.isInitializer()) { 1195 try writer.writeByte('('); 1196 try dg.renderType(writer, ty); 1197 try writer.writeByte(')'); 1198 } 1199 1200 const payload_val = if (val.castTag(.opt_payload)) |pl| pl.data else Value.undef; 1201 1202 try writer.writeAll("{ .payload = "); 1203 try dg.renderValue(writer, payload_ty, payload_val, initializer_type); 1204 try writer.writeAll(", .is_null = "); 1205 try dg.renderValue(writer, Type.bool, is_null_val, initializer_type); 1206 try writer.writeAll(" }"); 1207 }, 1208 .ErrorSet => { 1209 if (val.castTag(.@"error")) |error_pl| { 1210 // Error values are already defined by genErrDecls. 1211 try writer.print("zig_error_{}", .{fmtIdent(error_pl.data.name)}); 1212 } else { 1213 try writer.print("{}", .{try dg.fmtIntLiteral(ty, val, .Other)}); 1214 } 1215 }, 1216 .ErrorUnion => { 1217 const payload_ty = ty.errorUnionPayload(); 1218 const error_ty = ty.errorUnionSet(); 1219 const error_val = if (val.errorUnionIsPayload()) Value.zero else val; 1220 1221 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 1222 return dg.renderValue(writer, error_ty, error_val, location); 1223 } 1224 1225 if (!location.isInitializer()) { 1226 try writer.writeByte('('); 1227 try dg.renderType(writer, ty); 1228 try writer.writeByte(')'); 1229 } 1230 1231 const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.undef; 1232 try writer.writeAll("{ .payload = "); 1233 try dg.renderValue(writer, payload_ty, payload_val, initializer_type); 1234 try writer.writeAll(", .error = "); 1235 try dg.renderValue(writer, error_ty, error_val, initializer_type); 1236 try writer.writeAll(" }"); 1237 }, 1238 .Enum => { 1239 switch (val.tag()) { 1240 .enum_field_index => { 1241 const field_index = val.castTag(.enum_field_index).?.data; 1242 switch (ty.tag()) { 1243 .enum_simple => return writer.print("{d}", .{field_index}), 1244 .enum_full, .enum_nonexhaustive => { 1245 const enum_full = ty.cast(Type.Payload.EnumFull).?.data; 1246 if (enum_full.values.count() != 0) { 1247 const tag_val = enum_full.values.keys()[field_index]; 1248 return dg.renderValue(writer, enum_full.tag_ty, tag_val, location); 1249 } else { 1250 return writer.print("{d}", .{field_index}); 1251 } 1252 }, 1253 .enum_numbered => { 1254 const enum_obj = ty.castTag(.enum_numbered).?.data; 1255 if (enum_obj.values.count() != 0) { 1256 const tag_val = enum_obj.values.keys()[field_index]; 1257 return dg.renderValue(writer, enum_obj.tag_ty, tag_val, location); 1258 } else { 1259 return writer.print("{d}", .{field_index}); 1260 } 1261 }, 1262 else => unreachable, 1263 } 1264 }, 1265 else => { 1266 var int_tag_ty_buffer: Type.Payload.Bits = undefined; 1267 const int_tag_ty = ty.intTagType(&int_tag_ty_buffer); 1268 return dg.renderValue(writer, int_tag_ty, val, location); 1269 }, 1270 } 1271 }, 1272 .Fn => switch (val.tag()) { 1273 .function => { 1274 const decl = val.castTag(.function).?.data.owner_decl; 1275 return dg.renderDeclValue(writer, ty, val, decl, location); 1276 }, 1277 .extern_fn => { 1278 const decl = val.castTag(.extern_fn).?.data.owner_decl; 1279 return dg.renderDeclValue(writer, ty, val, decl, location); 1280 }, 1281 else => unreachable, 1282 }, 1283 .Struct => switch (ty.containerLayout()) { 1284 .Auto, .Extern => { 1285 const field_vals = val.castTag(.aggregate).?.data; 1286 1287 if (!location.isInitializer()) { 1288 try writer.writeByte('('); 1289 try dg.renderType(writer, ty); 1290 try writer.writeByte(')'); 1291 } 1292 1293 try writer.writeByte('{'); 1294 var empty = true; 1295 for (field_vals, 0..) |field_val, field_i| { 1296 if (ty.structFieldIsComptime(field_i)) continue; 1297 const field_ty = ty.structFieldType(field_i); 1298 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; 1299 1300 if (!empty) try writer.writeByte(','); 1301 try dg.renderValue(writer, field_ty, field_val, initializer_type); 1302 1303 empty = false; 1304 } 1305 try writer.writeByte('}'); 1306 }, 1307 .Packed => { 1308 const field_vals = val.castTag(.aggregate).?.data; 1309 const int_info = ty.intInfo(target); 1310 1311 var bit_offset_ty_pl = Type.Payload.Bits{ 1312 .base = .{ .tag = .int_unsigned }, 1313 .data = Type.smallestUnsignedBits(int_info.bits - 1), 1314 }; 1315 const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); 1316 1317 var bit_offset_val_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = 0 }; 1318 const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); 1319 1320 var eff_num_fields: usize = 0; 1321 for (0..field_vals.len) |field_i| { 1322 if (ty.structFieldIsComptime(field_i)) continue; 1323 const field_ty = ty.structFieldType(field_i); 1324 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; 1325 1326 eff_num_fields += 1; 1327 } 1328 1329 if (eff_num_fields == 0) { 1330 try writer.writeByte('('); 1331 try dg.renderValue(writer, ty, Value.undef, initializer_type); 1332 try writer.writeByte(')'); 1333 } else if (ty.bitSize(target) > 64) { 1334 // zig_or_u128(zig_or_u128(zig_shl_u128(a, a_off), zig_shl_u128(b, b_off)), zig_shl_u128(c, c_off)) 1335 var num_or = eff_num_fields - 1; 1336 while (num_or > 0) : (num_or -= 1) { 1337 try writer.writeAll("zig_or_"); 1338 try dg.renderTypeForBuiltinFnName(writer, ty); 1339 try writer.writeByte('('); 1340 } 1341 1342 var eff_index: usize = 0; 1343 var needs_closing_paren = false; 1344 for (field_vals, 0..) |field_val, field_i| { 1345 if (ty.structFieldIsComptime(field_i)) continue; 1346 const field_ty = ty.structFieldType(field_i); 1347 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; 1348 1349 const cast_context = IntCastContext{ .value = .{ .value = field_val } }; 1350 if (bit_offset_val_pl.data != 0) { 1351 try writer.writeAll("zig_shl_"); 1352 try dg.renderTypeForBuiltinFnName(writer, ty); 1353 try writer.writeByte('('); 1354 try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); 1355 try writer.writeAll(", "); 1356 try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); 1357 try writer.writeByte(')'); 1358 } else { 1359 try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); 1360 } 1361 1362 if (needs_closing_paren) try writer.writeByte(')'); 1363 if (eff_index != eff_num_fields - 1) try writer.writeAll(", "); 1364 1365 bit_offset_val_pl.data += field_ty.bitSize(target); 1366 needs_closing_paren = true; 1367 eff_index += 1; 1368 } 1369 } else { 1370 try writer.writeByte('('); 1371 // a << a_off | b << b_off | c << c_off 1372 var empty = true; 1373 for (field_vals, 0..) |field_val, field_i| { 1374 if (ty.structFieldIsComptime(field_i)) continue; 1375 const field_ty = ty.structFieldType(field_i); 1376 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; 1377 1378 if (!empty) try writer.writeAll(" | "); 1379 try writer.writeByte('('); 1380 try dg.renderType(writer, ty); 1381 try writer.writeByte(')'); 1382 1383 if (bit_offset_val_pl.data != 0) { 1384 try dg.renderValue(writer, field_ty, field_val, .Other); 1385 try writer.writeAll(" << "); 1386 try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); 1387 } else { 1388 try dg.renderValue(writer, field_ty, field_val, .Other); 1389 } 1390 1391 bit_offset_val_pl.data += field_ty.bitSize(target); 1392 empty = false; 1393 } 1394 try writer.writeByte(')'); 1395 } 1396 }, 1397 }, 1398 .Union => { 1399 const union_obj = val.castTag(.@"union").?.data; 1400 1401 if (!location.isInitializer()) { 1402 try writer.writeByte('('); 1403 try dg.renderType(writer, ty); 1404 try writer.writeByte(')'); 1405 } 1406 1407 const field_i = ty.unionTagFieldIndex(union_obj.tag, dg.module).?; 1408 const field_ty = ty.unionFields().values()[field_i].ty; 1409 const field_name = ty.unionFields().keys()[field_i]; 1410 if (ty.containerLayout() == .Packed) { 1411 if (field_ty.hasRuntimeBits()) { 1412 if (field_ty.isPtrAtRuntime()) { 1413 try writer.writeByte('('); 1414 try dg.renderType(writer, ty); 1415 try writer.writeByte(')'); 1416 } else if (field_ty.zigTypeTag() == .Float) { 1417 try writer.writeByte('('); 1418 try dg.renderType(writer, ty); 1419 try writer.writeByte(')'); 1420 } 1421 try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); 1422 } else { 1423 try writer.writeAll("0"); 1424 } 1425 return; 1426 } 1427 1428 try writer.writeByte('{'); 1429 if (ty.unionTagTypeSafety()) |tag_ty| { 1430 const layout = ty.unionGetLayout(target); 1431 if (layout.tag_size != 0) { 1432 try writer.writeAll(" .tag = "); 1433 try dg.renderValue(writer, tag_ty, union_obj.tag, initializer_type); 1434 } 1435 if (ty.unionHasAllZeroBitFieldTypes()) return try writer.writeByte('}'); 1436 if (layout.tag_size != 0) try writer.writeByte(','); 1437 try writer.writeAll(" .payload = {"); 1438 } 1439 if (field_ty.hasRuntimeBits()) { 1440 try writer.print(" .{ } = ", .{fmtIdent(field_name)}); 1441 try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); 1442 try writer.writeByte(' '); 1443 } else for (ty.unionFields().values()) |field| { 1444 if (!field.ty.hasRuntimeBits()) continue; 1445 try dg.renderValue(writer, field.ty, Value.undef, initializer_type); 1446 break; 1447 } 1448 if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); 1449 try writer.writeByte('}'); 1450 }, 1451 1452 .ComptimeInt => unreachable, 1453 .ComptimeFloat => unreachable, 1454 .Type => unreachable, 1455 .EnumLiteral => unreachable, 1456 .Void => unreachable, 1457 .NoReturn => unreachable, 1458 .Undefined => unreachable, 1459 .Null => unreachable, 1460 .Opaque => unreachable, 1461 1462 .Frame, 1463 .AnyFrame, 1464 => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ 1465 @tagName(tag), 1466 }), 1467 } 1468 } 1469 1470 fn renderFunctionSignature( 1471 dg: *DeclGen, 1472 w: anytype, 1473 fn_decl_index: Decl.Index, 1474 kind: CType.Kind, 1475 name: union(enum) { 1476 export_index: u32, 1477 string: []const u8, 1478 }, 1479 ) !void { 1480 const store = &dg.ctypes.set; 1481 const module = dg.module; 1482 1483 const fn_decl = module.declPtr(fn_decl_index); 1484 const fn_cty_idx = try dg.typeToIndex(fn_decl.ty, kind); 1485 1486 const fn_info = fn_decl.ty.fnInfo(); 1487 if (fn_info.cc == .Naked) { 1488 switch (kind) { 1489 .forward => try w.writeAll("zig_naked_decl "), 1490 .complete => try w.writeAll("zig_naked "), 1491 else => unreachable, 1492 } 1493 } 1494 if (fn_decl.val.castTag(.function)) |func_payload| 1495 if (func_payload.data.is_cold) try w.writeAll("zig_cold "); 1496 if (fn_info.return_type.tag() == .noreturn) try w.writeAll("zig_noreturn "); 1497 1498 const trailing = try renderTypePrefix( 1499 dg.decl_index, 1500 store.*, 1501 module, 1502 w, 1503 fn_cty_idx, 1504 .suffix, 1505 .{}, 1506 ); 1507 try w.print("{}", .{trailing}); 1508 1509 if (toCallingConvention(fn_info.cc)) |call_conv| { 1510 try w.print("zig_callconv({s}) ", .{call_conv}); 1511 } 1512 1513 switch (kind) { 1514 .forward => {}, 1515 .complete => if (fn_info.alignment > 0) 1516 try w.print(" zig_align_fn({})", .{fn_info.alignment}), 1517 else => unreachable, 1518 } 1519 1520 switch (name) { 1521 .export_index => |export_index| try dg.renderDeclName(w, fn_decl_index, export_index), 1522 .string => |string| try w.writeAll(string), 1523 } 1524 1525 try renderTypeSuffix( 1526 dg.decl_index, 1527 store.*, 1528 module, 1529 w, 1530 fn_cty_idx, 1531 .suffix, 1532 CQualifiers.init(.{ .@"const" = switch (kind) { 1533 .forward => false, 1534 .complete => true, 1535 else => unreachable, 1536 } }), 1537 ); 1538 1539 switch (kind) { 1540 .forward => if (fn_info.alignment > 0) 1541 try w.print(" zig_align_fn({})", .{fn_info.alignment}), 1542 .complete => {}, 1543 else => unreachable, 1544 } 1545 } 1546 1547 fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { 1548 return dg.ctypes.indexToCType(idx); 1549 } 1550 1551 fn typeToIndex(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType.Index { 1552 return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module, kind); 1553 } 1554 1555 fn typeToCType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType { 1556 return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind); 1557 } 1558 1559 fn byteSize(dg: *DeclGen, cty: CType) u64 { 1560 return cty.byteSize(dg.ctypes.set, dg.module.getTarget()); 1561 } 1562 1563 /// Renders a type as a single identifier, generating intermediate typedefs 1564 /// if necessary. 1565 /// 1566 /// This is guaranteed to be valid in both typedefs and declarations/definitions. 1567 /// 1568 /// There are three type formats in total that we support rendering: 1569 /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | 1570 /// |---------------------|-----------------|---------------------| 1571 /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | 1572 /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | 1573 /// 1574 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { 1575 try dg.renderCType(w, try dg.typeToIndex(t, .complete)); 1576 } 1577 1578 fn renderCType(dg: *DeclGen, w: anytype, idx: CType.Index) error{ OutOfMemory, AnalysisFail }!void { 1579 const store = &dg.ctypes.set; 1580 const module = dg.module; 1581 _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); 1582 try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); 1583 } 1584 1585 const IntCastContext = union(enum) { 1586 c_value: struct { 1587 f: *Function, 1588 value: CValue, 1589 v: Vectorize, 1590 }, 1591 value: struct { 1592 value: Value, 1593 }, 1594 1595 pub fn writeValue(self: *const IntCastContext, dg: *DeclGen, w: anytype, value_ty: Type, location: ValueRenderLocation) !void { 1596 switch (self.*) { 1597 .c_value => |v| { 1598 try v.f.writeCValue(w, v.value, location); 1599 try v.v.elem(v.f, w); 1600 }, 1601 .value => |v| { 1602 try dg.renderValue(w, value_ty, v.value, location); 1603 }, 1604 } 1605 } 1606 }; 1607 1608 /// Renders a cast to an int type, from either an int or a pointer. 1609 /// 1610 /// Some platforms don't have 128 bit integers, so we need to use 1611 /// the zig_make_ and zig_lo_ macros in those cases. 1612 /// 1613 /// | Dest type bits | Src type | Result 1614 /// |------------------|------------------|---------------------------| 1615 /// | < 64 bit integer | pointer | (zig_<dest_ty>)(zig_<u|i>size)src 1616 /// | < 64 bit integer | < 64 bit integer | (zig_<dest_ty>)src 1617 /// | < 64 bit integer | > 64 bit integer | zig_lo(src) 1618 /// | > 64 bit integer | pointer | zig_make_<dest_ty>(0, (zig_<u|i>size)src) 1619 /// | > 64 bit integer | < 64 bit integer | zig_make_<dest_ty>(0, src) 1620 /// | > 64 bit integer | > 64 bit integer | zig_make_<dest_ty>(zig_hi_<src_ty>(src), zig_lo_<src_ty>(src)) 1621 fn renderIntCast(dg: *DeclGen, w: anytype, dest_ty: Type, context: IntCastContext, src_ty: Type, location: ValueRenderLocation) !void { 1622 const target = dg.module.getTarget(); 1623 const dest_bits = dest_ty.bitSize(target); 1624 const dest_int_info = dest_ty.intInfo(target); 1625 1626 const src_is_ptr = src_ty.isPtrAtRuntime(); 1627 const src_eff_ty: Type = if (src_is_ptr) switch (dest_int_info.signedness) { 1628 .unsigned => Type.usize, 1629 .signed => Type.isize, 1630 } else src_ty; 1631 1632 const src_bits = src_eff_ty.bitSize(target); 1633 const src_int_info = if (src_eff_ty.isAbiInt()) src_eff_ty.intInfo(target) else null; 1634 if (dest_bits <= 64 and src_bits <= 64) { 1635 const needs_cast = src_int_info == null or 1636 (toCIntBits(dest_int_info.bits) != toCIntBits(src_int_info.?.bits) or 1637 dest_int_info.signedness != src_int_info.?.signedness); 1638 1639 if (needs_cast) { 1640 try w.writeByte('('); 1641 try dg.renderType(w, dest_ty); 1642 try w.writeByte(')'); 1643 } 1644 if (src_is_ptr) { 1645 try w.writeByte('('); 1646 try dg.renderType(w, src_eff_ty); 1647 try w.writeByte(')'); 1648 } 1649 try context.writeValue(dg, w, src_ty, location); 1650 } else if (dest_bits <= 64 and src_bits > 64) { 1651 assert(!src_is_ptr); 1652 if (dest_bits < 64) { 1653 try w.writeByte('('); 1654 try dg.renderType(w, dest_ty); 1655 try w.writeByte(')'); 1656 } 1657 try w.writeAll("zig_lo_"); 1658 try dg.renderTypeForBuiltinFnName(w, src_eff_ty); 1659 try w.writeByte('('); 1660 try context.writeValue(dg, w, src_ty, .FunctionArgument); 1661 try w.writeByte(')'); 1662 } else if (dest_bits > 64 and src_bits <= 64) { 1663 try w.writeAll("zig_make_"); 1664 try dg.renderTypeForBuiltinFnName(w, dest_ty); 1665 try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral? 1666 if (src_is_ptr) { 1667 try w.writeByte('('); 1668 try dg.renderType(w, src_eff_ty); 1669 try w.writeByte(')'); 1670 } 1671 try context.writeValue(dg, w, src_ty, .FunctionArgument); 1672 try w.writeByte(')'); 1673 } else { 1674 assert(!src_is_ptr); 1675 try w.writeAll("zig_make_"); 1676 try dg.renderTypeForBuiltinFnName(w, dest_ty); 1677 try w.writeAll("(zig_hi_"); 1678 try dg.renderTypeForBuiltinFnName(w, src_eff_ty); 1679 try w.writeByte('('); 1680 try context.writeValue(dg, w, src_ty, .FunctionArgument); 1681 try w.writeAll("), zig_lo_"); 1682 try dg.renderTypeForBuiltinFnName(w, src_eff_ty); 1683 try w.writeByte('('); 1684 try context.writeValue(dg, w, src_ty, .FunctionArgument); 1685 try w.writeAll("))"); 1686 } 1687 } 1688 1689 /// Renders a type and name in field declaration/definition format. 1690 /// 1691 /// There are three type formats in total that we support rendering: 1692 /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | 1693 /// |---------------------|-----------------|---------------------| 1694 /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | 1695 /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | 1696 /// 1697 fn renderTypeAndName( 1698 dg: *DeclGen, 1699 w: anytype, 1700 ty: Type, 1701 name: CValue, 1702 qualifiers: CQualifiers, 1703 alignment: u32, 1704 kind: CType.Kind, 1705 ) error{ OutOfMemory, AnalysisFail }!void { 1706 const target = dg.module.getTarget(); 1707 const alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)); 1708 try dg.renderCTypeAndName(w, try dg.typeToIndex(ty, kind), name, qualifiers, alignas); 1709 } 1710 1711 fn renderCTypeAndName( 1712 dg: *DeclGen, 1713 w: anytype, 1714 cty_idx: CType.Index, 1715 name: CValue, 1716 qualifiers: CQualifiers, 1717 alignas: CType.AlignAs, 1718 ) error{ OutOfMemory, AnalysisFail }!void { 1719 const store = &dg.ctypes.set; 1720 const module = dg.module; 1721 1722 switch (std.math.order(alignas.@"align", alignas.abi)) { 1723 .lt => try w.print("zig_under_align({}) ", .{alignas.getAlign()}), 1724 .eq => {}, 1725 .gt => try w.print("zig_align({}) ", .{alignas.getAlign()}), 1726 } 1727 1728 const trailing = 1729 try renderTypePrefix(dg.decl_index, store.*, module, w, cty_idx, .suffix, qualifiers); 1730 try w.print("{}", .{trailing}); 1731 try dg.writeCValue(w, name); 1732 try renderTypeSuffix(dg.decl_index, store.*, module, w, cty_idx, .suffix, .{}); 1733 } 1734 1735 fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { 1736 switch (tv.val.tag()) { 1737 .extern_fn => return true, 1738 .function => { 1739 const func = tv.val.castTag(.function).?.data; 1740 return dg.module.decl_exports.contains(func.owner_decl); 1741 }, 1742 .variable => { 1743 const variable = tv.val.castTag(.variable).?.data; 1744 return dg.module.decl_exports.contains(variable.owner_decl); 1745 }, 1746 else => unreachable, 1747 } 1748 } 1749 1750 fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void { 1751 switch (c_value) { 1752 .none => unreachable, 1753 .local, .new_local => |i| return w.print("t{d}", .{i}), 1754 .local_ref => |i| return w.print("&t{d}", .{i}), 1755 .constant => unreachable, 1756 .arg => |i| return w.print("a{d}", .{i}), 1757 .arg_array => |i| return dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }), 1758 .field => |i| return w.print("f{d}", .{i}), 1759 .decl => |decl| return dg.renderDeclName(w, decl, 0), 1760 .decl_ref => |decl| { 1761 try w.writeByte('&'); 1762 return dg.renderDeclName(w, decl, 0); 1763 }, 1764 .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other), 1765 .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), 1766 .payload_identifier => |ident| return w.print("{ }.{ }", .{ 1767 fmtIdent("payload"), 1768 fmtIdent(ident), 1769 }), 1770 } 1771 } 1772 1773 fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void { 1774 switch (c_value) { 1775 .none => unreachable, 1776 .local, .new_local => |i| return w.print("(*t{d})", .{i}), 1777 .local_ref => |i| return w.print("t{d}", .{i}), 1778 .constant => unreachable, 1779 .arg => |i| return w.print("(*a{d})", .{i}), 1780 .arg_array => |i| { 1781 try w.writeAll("(*"); 1782 try dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }); 1783 return w.writeByte(')'); 1784 }, 1785 .field => |i| return w.print("f{d}", .{i}), 1786 .decl => |decl| { 1787 try w.writeAll("(*"); 1788 try dg.renderDeclName(w, decl, 0); 1789 return w.writeByte(')'); 1790 }, 1791 .decl_ref => |decl| return dg.renderDeclName(w, decl, 0), 1792 .undef => unreachable, 1793 .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}), 1794 .payload_identifier => |ident| return w.print("(*{ }.{ })", .{ 1795 fmtIdent("payload"), 1796 fmtIdent(ident), 1797 }), 1798 } 1799 } 1800 1801 fn writeCValueMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { 1802 try dg.writeCValue(writer, c_value); 1803 try writer.writeByte('.'); 1804 try dg.writeCValue(writer, member); 1805 } 1806 1807 fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { 1808 switch (c_value) { 1809 .none, .constant, .field, .undef => unreachable, 1810 .new_local, .local, .arg, .arg_array, .decl, .identifier, .payload_identifier => { 1811 try dg.writeCValue(writer, c_value); 1812 try writer.writeAll("->"); 1813 }, 1814 .local_ref, .decl_ref => { 1815 try dg.writeCValueDeref(writer, c_value); 1816 try writer.writeByte('.'); 1817 }, 1818 } 1819 try dg.writeCValue(writer, member); 1820 } 1821 1822 fn renderFwdDecl(dg: *DeclGen, decl_index: Decl.Index, variable: *Module.Var) !void { 1823 const decl = dg.module.declPtr(decl_index); 1824 const fwd_decl_writer = dg.fwd_decl.writer(); 1825 const is_global = dg.declIsGlobal(.{ .ty = decl.ty, .val = decl.val }) or variable.is_extern; 1826 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); 1827 if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal "); 1828 if (variable.is_weak_linkage) try fwd_decl_writer.writeAll("zig_weak_linkage "); 1829 try dg.renderTypeAndName( 1830 fwd_decl_writer, 1831 decl.ty, 1832 .{ .decl = decl_index }, 1833 CQualifiers.init(.{ .@"const" = !variable.is_mutable }), 1834 decl.@"align", 1835 .complete, 1836 ); 1837 try fwd_decl_writer.writeAll(";\n"); 1838 } 1839 1840 fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: Decl.Index, export_index: u32) !void { 1841 const decl = dg.module.declPtr(decl_index); 1842 dg.module.markDeclAlive(decl); 1843 1844 if (dg.module.decl_exports.get(decl_index)) |exports| { 1845 try writer.writeAll(exports.items[export_index].options.name); 1846 } else if (decl.isExtern()) { 1847 try writer.writeAll(mem.span(decl.name)); 1848 } else { 1849 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), 1850 // expand to 3x the length of its input, but let's cut it off at a much shorter limit. 1851 var name: [100]u8 = undefined; 1852 var name_stream = std.io.fixedBufferStream(&name); 1853 decl.renderFullyQualifiedName(dg.module, name_stream.writer()) catch |err| switch (err) { 1854 error.NoSpaceLeft => {}, 1855 }; 1856 try writer.print("{}__{d}", .{ 1857 fmtIdent(name_stream.getWritten()), 1858 @enumToInt(decl_index), 1859 }); 1860 } 1861 } 1862 1863 fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void { 1864 try dg.renderCTypeForBuiltinFnName(writer, try dg.typeToCType(ty, .complete)); 1865 } 1866 1867 fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, cty: CType) !void { 1868 switch (cty.tag()) { 1869 else => try writer.print("{c}{d}", .{ 1870 if (cty.isBool()) 1871 signAbbrev(.unsigned) 1872 else if (cty.isInteger()) 1873 signAbbrev(cty.signedness() orelse .unsigned) 1874 else if (cty.isFloat()) 1875 @as(u8, 'f') 1876 else if (cty.isPointer()) 1877 @as(u8, 'p') 1878 else 1879 return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{ 1880 cty.tag(), 1881 }), 1882 if (cty.isFloat()) cty.floatActiveBits(dg.module.getTarget()) else dg.byteSize(cty) * 8, 1883 }), 1884 .array => try writer.writeAll("big"), 1885 } 1886 } 1887 1888 fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void { 1889 const cty = try dg.typeToCType(ty, .complete); 1890 const is_big = cty.tag() == .array; 1891 1892 switch (info) { 1893 .none => if (!is_big) return, 1894 .bits => {}, 1895 } 1896 1897 const target = dg.module.getTarget(); 1898 const int_info = if (ty.isAbiInt()) ty.intInfo(target) else std.builtin.Type.Int{ 1899 .signedness = .unsigned, 1900 .bits = @intCast(u16, ty.bitSize(target)), 1901 }; 1902 1903 if (is_big) try writer.print(", {}", .{int_info.signedness == .signed}); 1904 1905 var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits }; 1906 try writer.print(", {}", .{try dg.fmtIntLiteral( 1907 if (is_big) Type.u16 else Type.u8, 1908 Value.initPayload(&bits_pl.base), 1909 .FunctionArgument, 1910 )}); 1911 } 1912 1913 fn fmtIntLiteral( 1914 dg: *DeclGen, 1915 ty: Type, 1916 val: Value, 1917 loc: ValueRenderLocation, 1918 ) !std.fmt.Formatter(formatIntLiteral) { 1919 const kind: CType.Kind = switch (loc) { 1920 .FunctionArgument => .parameter, 1921 .Initializer, .Other => .complete, 1922 .StaticInitializer => .global, 1923 }; 1924 return std.fmt.Formatter(formatIntLiteral){ .data = .{ 1925 .dg = dg, 1926 .int_info = ty.intInfo(dg.module.getTarget()), 1927 .kind = kind, 1928 .cty = try dg.typeToCType(ty, kind), 1929 .val = val, 1930 } }; 1931 } 1932 }; 1933 1934 const CTypeFix = enum { prefix, suffix }; 1935 const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); 1936 const Const = CQualifiers.init(.{ .@"const" = true }); 1937 const RenderCTypeTrailing = enum { 1938 no_space, 1939 maybe_space, 1940 1941 pub fn format( 1942 self: @This(), 1943 comptime fmt: []const u8, 1944 _: std.fmt.FormatOptions, 1945 w: anytype, 1946 ) @TypeOf(w).Error!void { 1947 if (fmt.len != 0) 1948 @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ 1949 @typeName(@This()) ++ "'"); 1950 comptime assert(fmt.len == 0); 1951 switch (self) { 1952 .no_space => {}, 1953 .maybe_space => try w.writeByte(' '), 1954 } 1955 } 1956 }; 1957 fn renderTypeName( 1958 mod: *Module, 1959 w: anytype, 1960 idx: CType.Index, 1961 cty: CType, 1962 attributes: []const u8, 1963 ) !void { 1964 switch (cty.tag()) { 1965 else => unreachable, 1966 1967 .fwd_anon_struct, 1968 .fwd_anon_union, 1969 => |tag| try w.print("{s} {s}anon__lazy_{d}", .{ 1970 @tagName(tag)["fwd_anon_".len..], 1971 attributes, 1972 idx, 1973 }), 1974 1975 .fwd_struct, 1976 .fwd_union, 1977 => |tag| { 1978 const owner_decl = cty.cast(CType.Payload.FwdDecl).?.data; 1979 try w.print("{s} {s}{}__{d}", .{ 1980 @tagName(tag)["fwd_".len..], 1981 attributes, 1982 fmtIdent(mem.span(mod.declPtr(owner_decl).name)), 1983 @enumToInt(owner_decl), 1984 }); 1985 }, 1986 } 1987 } 1988 fn renderTypePrefix( 1989 decl: Decl.OptionalIndex, 1990 store: CType.Store.Set, 1991 mod: *Module, 1992 w: anytype, 1993 idx: CType.Index, 1994 parent_fix: CTypeFix, 1995 qualifiers: CQualifiers, 1996 ) @TypeOf(w).Error!RenderCTypeTrailing { 1997 var trailing = RenderCTypeTrailing.maybe_space; 1998 1999 const cty = store.indexToCType(idx); 2000 switch (cty.tag()) { 2001 .void, 2002 .char, 2003 .@"signed char", 2004 .short, 2005 .int, 2006 .long, 2007 .@"long long", 2008 ._Bool, 2009 .@"unsigned char", 2010 .@"unsigned short", 2011 .@"unsigned int", 2012 .@"unsigned long", 2013 .@"unsigned long long", 2014 .float, 2015 .double, 2016 .@"long double", 2017 .bool, 2018 .size_t, 2019 .ptrdiff_t, 2020 .uint8_t, 2021 .int8_t, 2022 .uint16_t, 2023 .int16_t, 2024 .uint32_t, 2025 .int32_t, 2026 .uint64_t, 2027 .int64_t, 2028 .uintptr_t, 2029 .intptr_t, 2030 .zig_u128, 2031 .zig_i128, 2032 .zig_f16, 2033 .zig_f32, 2034 .zig_f64, 2035 .zig_f80, 2036 .zig_f128, 2037 .zig_c_longdouble, 2038 => |tag| try w.writeAll(@tagName(tag)), 2039 2040 .pointer, 2041 .pointer_const, 2042 .pointer_volatile, 2043 .pointer_const_volatile, 2044 => |tag| { 2045 const child_idx = cty.cast(CType.Payload.Child).?.data; 2046 const child_trailing = try renderTypePrefix( 2047 decl, 2048 store, 2049 mod, 2050 w, 2051 child_idx, 2052 .prefix, 2053 CQualifiers.init(.{ .@"const" = switch (tag) { 2054 .pointer, .pointer_volatile => false, 2055 .pointer_const, .pointer_const_volatile => true, 2056 else => unreachable, 2057 }, .@"volatile" = switch (tag) { 2058 .pointer, .pointer_const => false, 2059 .pointer_volatile, .pointer_const_volatile => true, 2060 else => unreachable, 2061 } }), 2062 ); 2063 try w.print("{}*", .{child_trailing}); 2064 trailing = .no_space; 2065 }, 2066 2067 .array, 2068 .vector, 2069 => { 2070 const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type; 2071 const child_trailing = try renderTypePrefix( 2072 decl, 2073 store, 2074 mod, 2075 w, 2076 child_idx, 2077 .suffix, 2078 qualifiers, 2079 ); 2080 switch (parent_fix) { 2081 .prefix => { 2082 try w.print("{}(", .{child_trailing}); 2083 return .no_space; 2084 }, 2085 .suffix => return child_trailing, 2086 } 2087 }, 2088 2089 .fwd_anon_struct, 2090 .fwd_anon_union, 2091 => if (decl.unwrap()) |decl_index| 2092 try w.print("anon__{d}_{d}", .{ @enumToInt(decl_index), idx }) 2093 else 2094 try renderTypeName(mod, w, idx, cty, ""), 2095 2096 .fwd_struct, 2097 .fwd_union, 2098 => try renderTypeName(mod, w, idx, cty, ""), 2099 2100 .unnamed_struct, 2101 .unnamed_union, 2102 .packed_unnamed_struct, 2103 .packed_unnamed_union, 2104 => |tag| { 2105 try w.print("{s} {s}", .{ 2106 @tagName(tag)["unnamed_".len..], 2107 if (cty.isPacked()) "zig_packed(" else "", 2108 }); 2109 try renderAggregateFields(mod, w, store, cty, 1); 2110 if (cty.isPacked()) try w.writeByte(')'); 2111 }, 2112 2113 .anon_struct, 2114 .anon_union, 2115 .@"struct", 2116 .@"union", 2117 .packed_struct, 2118 .packed_union, 2119 => return renderTypePrefix( 2120 decl, 2121 store, 2122 mod, 2123 w, 2124 cty.cast(CType.Payload.Aggregate).?.data.fwd_decl, 2125 parent_fix, 2126 qualifiers, 2127 ), 2128 2129 .function, 2130 .varargs_function, 2131 => { 2132 const child_trailing = try renderTypePrefix( 2133 decl, 2134 store, 2135 mod, 2136 w, 2137 cty.cast(CType.Payload.Function).?.data.return_type, 2138 .suffix, 2139 .{}, 2140 ); 2141 switch (parent_fix) { 2142 .prefix => { 2143 try w.print("{}(", .{child_trailing}); 2144 return .no_space; 2145 }, 2146 .suffix => return child_trailing, 2147 } 2148 }, 2149 } 2150 2151 var qualifier_it = qualifiers.iterator(); 2152 while (qualifier_it.next()) |qualifier| { 2153 try w.print("{}{s}", .{ trailing, @tagName(qualifier) }); 2154 trailing = .maybe_space; 2155 } 2156 2157 return trailing; 2158 } 2159 fn renderTypeSuffix( 2160 decl: Decl.OptionalIndex, 2161 store: CType.Store.Set, 2162 mod: *Module, 2163 w: anytype, 2164 idx: CType.Index, 2165 parent_fix: CTypeFix, 2166 qualifiers: CQualifiers, 2167 ) @TypeOf(w).Error!void { 2168 const cty = store.indexToCType(idx); 2169 switch (cty.tag()) { 2170 .void, 2171 .char, 2172 .@"signed char", 2173 .short, 2174 .int, 2175 .long, 2176 .@"long long", 2177 ._Bool, 2178 .@"unsigned char", 2179 .@"unsigned short", 2180 .@"unsigned int", 2181 .@"unsigned long", 2182 .@"unsigned long long", 2183 .float, 2184 .double, 2185 .@"long double", 2186 .bool, 2187 .size_t, 2188 .ptrdiff_t, 2189 .uint8_t, 2190 .int8_t, 2191 .uint16_t, 2192 .int16_t, 2193 .uint32_t, 2194 .int32_t, 2195 .uint64_t, 2196 .int64_t, 2197 .uintptr_t, 2198 .intptr_t, 2199 .zig_u128, 2200 .zig_i128, 2201 .zig_f16, 2202 .zig_f32, 2203 .zig_f64, 2204 .zig_f80, 2205 .zig_f128, 2206 .zig_c_longdouble, 2207 => {}, 2208 2209 .pointer, 2210 .pointer_const, 2211 .pointer_volatile, 2212 .pointer_const_volatile, 2213 => try renderTypeSuffix( 2214 decl, 2215 store, 2216 mod, 2217 w, 2218 cty.cast(CType.Payload.Child).?.data, 2219 .prefix, 2220 .{}, 2221 ), 2222 2223 .array, 2224 .vector, 2225 => { 2226 switch (parent_fix) { 2227 .prefix => try w.writeByte(')'), 2228 .suffix => {}, 2229 } 2230 2231 try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len}); 2232 try renderTypeSuffix( 2233 decl, 2234 store, 2235 mod, 2236 w, 2237 cty.cast(CType.Payload.Sequence).?.data.elem_type, 2238 .suffix, 2239 .{}, 2240 ); 2241 }, 2242 2243 .fwd_anon_struct, 2244 .fwd_anon_union, 2245 .fwd_struct, 2246 .fwd_union, 2247 .unnamed_struct, 2248 .unnamed_union, 2249 .packed_unnamed_struct, 2250 .packed_unnamed_union, 2251 .anon_struct, 2252 .anon_union, 2253 .@"struct", 2254 .@"union", 2255 .packed_struct, 2256 .packed_union, 2257 => {}, 2258 2259 .function, 2260 .varargs_function, 2261 => |tag| { 2262 switch (parent_fix) { 2263 .prefix => try w.writeByte(')'), 2264 .suffix => {}, 2265 } 2266 2267 const data = cty.cast(CType.Payload.Function).?.data; 2268 2269 try w.writeByte('('); 2270 var need_comma = false; 2271 for (data.param_types, 0..) |param_type, param_i| { 2272 if (need_comma) try w.writeAll(", "); 2273 need_comma = true; 2274 const trailing = 2275 try renderTypePrefix(decl, store, mod, w, param_type, .suffix, qualifiers); 2276 if (qualifiers.contains(.@"const")) try w.print("{}a{d}", .{ trailing, param_i }); 2277 try renderTypeSuffix(decl, store, mod, w, param_type, .suffix, .{}); 2278 } 2279 switch (tag) { 2280 .function => {}, 2281 .varargs_function => { 2282 if (need_comma) try w.writeAll(", "); 2283 need_comma = true; 2284 try w.writeAll("..."); 2285 }, 2286 else => unreachable, 2287 } 2288 if (!need_comma) try w.writeAll("void"); 2289 try w.writeByte(')'); 2290 2291 try renderTypeSuffix(decl, store, mod, w, data.return_type, .suffix, .{}); 2292 }, 2293 } 2294 } 2295 fn renderAggregateFields( 2296 mod: *Module, 2297 writer: anytype, 2298 store: CType.Store.Set, 2299 cty: CType, 2300 indent: usize, 2301 ) !void { 2302 try writer.writeAll("{\n"); 2303 const fields = cty.fields(); 2304 for (fields) |field| { 2305 try writer.writeByteNTimes(' ', indent + 1); 2306 switch (std.math.order(field.alignas.@"align", field.alignas.abi)) { 2307 .lt => try writer.print("zig_under_align({}) ", .{field.alignas.getAlign()}), 2308 .eq => {}, 2309 .gt => try writer.print("zig_align({}) ", .{field.alignas.getAlign()}), 2310 } 2311 const trailing = try renderTypePrefix(.none, store, mod, writer, field.type, .suffix, .{}); 2312 try writer.print("{}{ }", .{ trailing, fmtIdent(mem.span(field.name)) }); 2313 try renderTypeSuffix(.none, store, mod, writer, field.type, .suffix, .{}); 2314 try writer.writeAll(";\n"); 2315 } 2316 try writer.writeByteNTimes(' ', indent); 2317 try writer.writeByte('}'); 2318 } 2319 2320 pub fn genTypeDecl( 2321 mod: *Module, 2322 writer: anytype, 2323 global_store: CType.Store.Set, 2324 global_idx: CType.Index, 2325 decl: Decl.OptionalIndex, 2326 decl_store: CType.Store.Set, 2327 decl_idx: CType.Index, 2328 found_existing: bool, 2329 ) !void { 2330 const global_cty = global_store.indexToCType(global_idx); 2331 switch (global_cty.tag()) { 2332 .fwd_anon_struct => if (decl != .none) { 2333 try writer.writeAll("typedef "); 2334 _ = try renderTypePrefix(.none, global_store, mod, writer, global_idx, .suffix, .{}); 2335 try writer.writeByte(' '); 2336 _ = try renderTypePrefix(decl, decl_store, mod, writer, decl_idx, .suffix, .{}); 2337 try writer.writeAll(";\n"); 2338 }, 2339 2340 .fwd_struct, 2341 .fwd_union, 2342 .anon_struct, 2343 .anon_union, 2344 .@"struct", 2345 .@"union", 2346 .packed_struct, 2347 .packed_union, 2348 => |tag| if (!found_existing) { 2349 switch (tag) { 2350 .fwd_struct, 2351 .fwd_union, 2352 => { 2353 const owner_decl = global_cty.cast(CType.Payload.FwdDecl).?.data; 2354 _ = try renderTypePrefix(.none, global_store, mod, writer, global_idx, .suffix, .{}); 2355 try writer.writeAll("; // "); 2356 try mod.declPtr(owner_decl).renderFullyQualifiedName(mod, writer); 2357 try writer.writeByte('\n'); 2358 }, 2359 2360 .anon_struct, 2361 .anon_union, 2362 .@"struct", 2363 .@"union", 2364 .packed_struct, 2365 .packed_union, 2366 => { 2367 const fwd_idx = global_cty.cast(CType.Payload.Aggregate).?.data.fwd_decl; 2368 try renderTypeName( 2369 mod, 2370 writer, 2371 fwd_idx, 2372 global_store.indexToCType(fwd_idx), 2373 if (global_cty.isPacked()) "zig_packed(" else "", 2374 ); 2375 try writer.writeByte(' '); 2376 try renderAggregateFields(mod, writer, global_store, global_cty, 0); 2377 if (global_cty.isPacked()) try writer.writeByte(')'); 2378 try writer.writeAll(";\n"); 2379 }, 2380 2381 else => unreachable, 2382 } 2383 }, 2384 2385 else => {}, 2386 } 2387 } 2388 2389 pub fn genGlobalAsm(mod: *Module, writer: anytype) !void { 2390 var it = mod.global_assembly.valueIterator(); 2391 while (it.next()) |asm_source| try writer.print("__asm({s});\n", .{fmtStringLiteral(asm_source.*, null)}); 2392 } 2393 2394 pub fn genErrDecls(o: *Object) !void { 2395 const writer = o.writer(); 2396 2397 try writer.writeAll("enum {\n"); 2398 o.indent_writer.pushIndent(); 2399 var max_name_len: usize = 0; 2400 for (o.dg.module.error_name_list.items, 0..) |name, value| { 2401 max_name_len = std.math.max(name.len, max_name_len); 2402 var err_pl = Value.Payload.Error{ .data = .{ .name = name } }; 2403 try o.dg.renderValue(writer, Type.anyerror, Value.initPayload(&err_pl.base), .Other); 2404 try writer.print(" = {d}u,\n", .{value}); 2405 } 2406 o.indent_writer.popIndent(); 2407 try writer.writeAll("};\n"); 2408 2409 const array_identifier = "zig_errorName"; 2410 const name_prefix = array_identifier ++ "_"; 2411 const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len); 2412 defer o.dg.gpa.free(name_buf); 2413 2414 @memcpy(name_buf[0..name_prefix.len], name_prefix); 2415 for (o.dg.module.error_name_list.items) |name| { 2416 @memcpy(name_buf[name_prefix.len..][0..name.len], name); 2417 const identifier = name_buf[0 .. name_prefix.len + name.len]; 2418 2419 var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; 2420 const name_ty = Type.initPayload(&name_ty_pl.base); 2421 2422 var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; 2423 const name_val = Value.initPayload(&name_pl.base); 2424 2425 try writer.writeAll("static "); 2426 try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, Const, 0, .complete); 2427 try writer.writeAll(" = "); 2428 try o.dg.renderValue(writer, name_ty, name_val, .StaticInitializer); 2429 try writer.writeAll(";\n"); 2430 } 2431 2432 var name_array_ty_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ 2433 .len = o.dg.module.error_name_list.items.len, 2434 .elem_type = Type.initTag(.const_slice_u8_sentinel_0), 2435 } }; 2436 const name_array_ty = Type.initPayload(&name_array_ty_pl.base); 2437 2438 try writer.writeAll("static "); 2439 try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = array_identifier }, Const, 0, .complete); 2440 try writer.writeAll(" = {"); 2441 for (o.dg.module.error_name_list.items, 0..) |name, value| { 2442 if (value != 0) try writer.writeByte(','); 2443 2444 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; 2445 const len_val = Value.initPayload(&len_pl.base); 2446 2447 try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ 2448 fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other), 2449 }); 2450 } 2451 try writer.writeAll("};\n"); 2452 } 2453 2454 fn genExports(o: *Object) !void { 2455 const tracy = trace(@src()); 2456 defer tracy.end(); 2457 2458 const fwd_decl_writer = o.dg.fwd_decl.writer(); 2459 if (o.dg.module.decl_exports.get(o.dg.decl_index.unwrap().?)) |exports| { 2460 for (exports.items[1..], 1..) |@"export", i| { 2461 try fwd_decl_writer.writeAll("zig_export("); 2462 try o.dg.renderFunctionSignature(fwd_decl_writer, o.dg.decl_index.unwrap().?, .forward, .{ .export_index = @intCast(u32, i) }); 2463 try fwd_decl_writer.print(", {s}, {s});\n", .{ 2464 fmtStringLiteral(exports.items[0].options.name, null), 2465 fmtStringLiteral(@"export".options.name, null), 2466 }); 2467 } 2468 } 2469 } 2470 2471 pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { 2472 const w = o.writer(); 2473 const key = lazy_fn.key_ptr.*; 2474 const val = lazy_fn.value_ptr; 2475 const fn_name = val.fn_name; 2476 switch (key) { 2477 .tag_name => { 2478 const enum_ty = val.data.tag_name; 2479 2480 const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); 2481 2482 try w.writeAll("static "); 2483 try o.dg.renderType(w, name_slice_ty); 2484 try w.writeByte(' '); 2485 try w.writeAll(fn_name); 2486 try w.writeByte('('); 2487 try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, 0, .complete); 2488 try w.writeAll(") {\n switch (tag) {\n"); 2489 for (enum_ty.enumFields().keys(), 0..) |name, index| { 2490 var tag_pl: Value.Payload.U32 = .{ 2491 .base = .{ .tag = .enum_field_index }, 2492 .data = @intCast(u32, index), 2493 }; 2494 const tag_val = Value.initPayload(&tag_pl.base); 2495 2496 var int_pl: Value.Payload.U64 = undefined; 2497 const int_val = tag_val.enumToInt(enum_ty, &int_pl); 2498 2499 var name_ty_pl = Type.Payload.Len{ 2500 .base = .{ .tag = .array_u8_sentinel_0 }, 2501 .data = name.len, 2502 }; 2503 const name_ty = Type.initPayload(&name_ty_pl.base); 2504 2505 var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; 2506 const name_val = Value.initPayload(&name_pl.base); 2507 2508 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; 2509 const len_val = Value.initPayload(&len_pl.base); 2510 2511 try w.print(" case {}: {{\n static ", .{ 2512 try o.dg.fmtIntLiteral(enum_ty, int_val, .Other), 2513 }); 2514 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete); 2515 try w.writeAll(" = "); 2516 try o.dg.renderValue(w, name_ty, name_val, .Initializer); 2517 try w.writeAll(";\n return ("); 2518 try o.dg.renderType(w, name_slice_ty); 2519 try w.print("){{{}, {}}};\n", .{ 2520 fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val, .Other), 2521 }); 2522 2523 try w.writeAll(" }\n"); 2524 } 2525 try w.writeAll(" }\n while ("); 2526 try o.dg.renderValue(w, Type.bool, Value.true, .Other); 2527 try w.writeAll(") "); 2528 _ = try airBreakpoint(w); 2529 try w.writeAll("}\n"); 2530 }, 2531 .never_tail, .never_inline => |fn_decl_index| { 2532 const fn_decl = o.dg.module.declPtr(fn_decl_index); 2533 const fn_cty = try o.dg.typeToCType(fn_decl.ty, .complete); 2534 const fn_info = fn_cty.cast(CType.Payload.Function).?.data; 2535 2536 const fwd_decl_writer = o.dg.fwd_decl.writer(); 2537 try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); 2538 try o.dg.renderFunctionSignature( 2539 fwd_decl_writer, 2540 fn_decl_index, 2541 .forward, 2542 .{ .string = fn_name }, 2543 ); 2544 try fwd_decl_writer.writeAll(";\n"); 2545 2546 try w.print("static zig_{s} ", .{@tagName(key)}); 2547 try o.dg.renderFunctionSignature(w, fn_decl_index, .complete, .{ .string = fn_name }); 2548 try w.writeAll(" {\n return "); 2549 try o.dg.renderDeclName(w, fn_decl_index, 0); 2550 try w.writeByte('('); 2551 for (0..fn_info.param_types.len) |arg| { 2552 if (arg > 0) try w.writeAll(", "); 2553 try o.dg.writeCValue(w, .{ .arg = arg }); 2554 } 2555 try w.writeAll(");\n}\n"); 2556 }, 2557 } 2558 } 2559 2560 pub fn genFunc(f: *Function) !void { 2561 const tracy = trace(@src()); 2562 defer tracy.end(); 2563 2564 const o = &f.object; 2565 const gpa = o.dg.gpa; 2566 const decl_index = o.dg.decl_index.unwrap().?; 2567 const tv: TypedValue = .{ 2568 .ty = o.dg.decl.?.ty, 2569 .val = o.dg.decl.?.val, 2570 }; 2571 2572 o.code_header = std.ArrayList(u8).init(gpa); 2573 defer o.code_header.deinit(); 2574 2575 const is_global = o.dg.declIsGlobal(tv); 2576 const fwd_decl_writer = o.dg.fwd_decl.writer(); 2577 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); 2578 try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 }); 2579 try fwd_decl_writer.writeAll(";\n"); 2580 try genExports(o); 2581 2582 try o.indent_writer.insertNewline(); 2583 if (!is_global) try o.writer().writeAll("static "); 2584 try o.dg.renderFunctionSignature(o.writer(), decl_index, .complete, .{ .export_index = 0 }); 2585 try o.writer().writeByte(' '); 2586 2587 // In case we need to use the header, populate it with a copy of the function 2588 // signature here. We anticipate a brace, newline, and space. 2589 try o.code_header.ensureUnusedCapacity(o.code.items.len + 3); 2590 o.code_header.appendSliceAssumeCapacity(o.code.items); 2591 o.code_header.appendSliceAssumeCapacity("{\n "); 2592 const empty_header_len = o.code_header.items.len; 2593 2594 f.free_locals_map.clearRetainingCapacity(); 2595 2596 const main_body = f.air.getMainBody(); 2597 try genBodyResolveState(f, undefined, &.{}, main_body, false); 2598 2599 try o.indent_writer.insertNewline(); 2600 2601 // Take advantage of the free_locals map to bucket locals per type. All 2602 // locals corresponding to AIR instructions should be in there due to 2603 // Liveness analysis, however, locals from alloc instructions will be 2604 // missing. These are added now to complete the map. Then we can sort by 2605 // alignment, descending. 2606 const free_locals = &f.free_locals_map; 2607 assert(f.value_map.count() == 0); // there must not be any unfreed locals 2608 for (f.allocs.keys(), f.allocs.values()) |local_index, should_emit| { 2609 if (!should_emit) continue; 2610 const local = f.locals.items[local_index]; 2611 log.debug("inserting local {d} into free_locals", .{local_index}); 2612 const gop = try free_locals.getOrPut(gpa, local.getType()); 2613 if (!gop.found_existing) gop.value_ptr.* = .{}; 2614 try gop.value_ptr.putNoClobber(gpa, local_index, {}); 2615 } 2616 2617 const SortContext = struct { 2618 keys: []const LocalType, 2619 2620 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { 2621 const lhs_ty = ctx.keys[lhs_index]; 2622 const rhs_ty = ctx.keys[rhs_index]; 2623 return lhs_ty.alignas.getAlign() > rhs_ty.alignas.getAlign(); 2624 } 2625 }; 2626 free_locals.sort(SortContext{ .keys = free_locals.keys() }); 2627 2628 const w = o.code_header.writer(); 2629 for (free_locals.values()) |list| { 2630 for (list.keys()) |local_index| { 2631 const local = f.locals.items[local_index]; 2632 try o.dg.renderCTypeAndName(w, local.cty_idx, .{ .local = local_index }, .{}, local.alignas); 2633 try w.writeAll(";\n "); 2634 } 2635 } 2636 2637 // If we have a header to insert, append the body to the header 2638 // and then return the result, freeing the body. 2639 if (o.code_header.items.len > empty_header_len) { 2640 try o.code_header.appendSlice(o.code.items[empty_header_len..]); 2641 mem.swap(std.ArrayList(u8), &o.code, &o.code_header); 2642 } 2643 } 2644 2645 pub fn genDecl(o: *Object) !void { 2646 const tracy = trace(@src()); 2647 defer tracy.end(); 2648 2649 const decl = o.dg.decl.?; 2650 const decl_c_value = .{ .decl = o.dg.decl_index.unwrap().? }; 2651 const tv: TypedValue = .{ .ty = decl.ty, .val = decl.val }; 2652 2653 if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime()) return; 2654 if (tv.val.tag() == .extern_fn) { 2655 const fwd_decl_writer = o.dg.fwd_decl.writer(); 2656 try fwd_decl_writer.writeAll("zig_extern "); 2657 try o.dg.renderFunctionSignature(fwd_decl_writer, decl_c_value.decl, .forward, .{ .export_index = 0 }); 2658 try fwd_decl_writer.writeAll(";\n"); 2659 try genExports(o); 2660 } else if (tv.val.castTag(.variable)) |var_payload| { 2661 const variable: *Module.Var = var_payload.data; 2662 try o.dg.renderFwdDecl(decl_c_value.decl, variable); 2663 try genExports(o); 2664 2665 if (variable.is_extern) return; 2666 2667 const is_global = o.dg.declIsGlobal(tv) or variable.is_extern; 2668 const w = o.writer(); 2669 if (!is_global) try w.writeAll("static "); 2670 if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); 2671 if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage "); 2672 if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); 2673 try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .{}, decl.@"align", .complete); 2674 if (decl.@"linksection" != null) try w.writeAll(", read, write)"); 2675 try w.writeAll(" = "); 2676 try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); 2677 try w.writeByte(';'); 2678 try o.indent_writer.insertNewline(); 2679 } else { 2680 const is_global = o.dg.module.decl_exports.contains(decl_c_value.decl); 2681 const fwd_decl_writer = o.dg.fwd_decl.writer(); 2682 2683 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); 2684 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, decl.@"align", .complete); 2685 try fwd_decl_writer.writeAll(";\n"); 2686 2687 const w = o.writer(); 2688 if (!is_global) try w.writeAll("static "); 2689 if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); 2690 try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, Const, decl.@"align", .complete); 2691 if (decl.@"linksection" != null) try w.writeAll(", read)"); 2692 try w.writeAll(" = "); 2693 try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); 2694 try w.writeAll(";\n"); 2695 } 2696 } 2697 2698 pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { 2699 const tracy = trace(@src()); 2700 defer tracy.end(); 2701 2702 const tv: TypedValue = .{ 2703 .ty = dg.decl.?.ty, 2704 .val = dg.decl.?.val, 2705 }; 2706 const writer = dg.fwd_decl.writer(); 2707 2708 switch (tv.ty.zigTypeTag()) { 2709 .Fn => { 2710 const is_global = dg.declIsGlobal(tv); 2711 if (is_global) { 2712 try writer.writeAll("zig_extern "); 2713 try dg.renderFunctionSignature(writer, dg.decl_index.unwrap().?, .complete, .{ .export_index = 0 }); 2714 try dg.fwd_decl.appendSlice(";\n"); 2715 } 2716 }, 2717 else => {}, 2718 } 2719 } 2720 2721 /// Generate code for an entire body which ends with a `noreturn` instruction. The states of 2722 /// `value_map` and `free_locals_map` are undefined after the generation, and new locals may not 2723 /// have been added to `free_locals_map`. For a version of this function that restores this state, 2724 /// see `genBodyResolveState`. 2725 fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void { 2726 const writer = f.object.writer(); 2727 if (body.len == 0) { 2728 try writer.writeAll("{}"); 2729 } else { 2730 try writer.writeAll("{\n"); 2731 f.object.indent_writer.pushIndent(); 2732 try genBodyInner(f, body); 2733 f.object.indent_writer.popIndent(); 2734 try writer.writeByte('}'); 2735 } 2736 } 2737 2738 /// Generate code for an entire body which ends with a `noreturn` instruction. The states of 2739 /// `value_map` and `free_locals_map` are restored to their original values, and any non-allocated 2740 /// locals introduced within the body are correctly added to `free_locals_map`. Operands in 2741 /// `leading_deaths` have their deaths processed before the body is generated. 2742 /// A scope is introduced (using braces) only if `inner` is `false`. 2743 /// If `leading_deaths` is empty, `inst` may be `undefined`. 2744 fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) error{ AnalysisFail, OutOfMemory }!void { 2745 if (body.len == 0) { 2746 // Don't go to the expense of cloning everything! 2747 if (!inner) try f.object.writer().writeAll("{}"); 2748 return; 2749 } 2750 2751 // TODO: we can probably avoid the copies in some other common cases too. 2752 2753 const gpa = f.object.dg.gpa; 2754 2755 // Save the original value_map and free_locals_map so that we can restore them after the body. 2756 var old_value_map = try f.value_map.clone(); 2757 defer old_value_map.deinit(); 2758 var old_free_locals = try cloneFreeLocalsMap(gpa, &f.free_locals_map); 2759 defer deinitFreeLocalsMap(gpa, &old_free_locals); 2760 2761 // Remember how many locals there were before entering the body so that we can free any that 2762 // were newly introduced. Any new locals must necessarily be logically free after the then 2763 // branch is complete. 2764 const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); 2765 2766 for (leading_deaths) |death| { 2767 try die(f, inst, Air.indexToRef(death)); 2768 } 2769 2770 if (inner) { 2771 try genBodyInner(f, body); 2772 } else { 2773 try genBody(f, body); 2774 } 2775 2776 f.value_map.deinit(); 2777 f.value_map = old_value_map.move(); 2778 deinitFreeLocalsMap(gpa, &f.free_locals_map); 2779 f.free_locals_map = old_free_locals.move(); 2780 2781 // Now, use the lengths we stored earlier to detect any locals the body generated, and free 2782 // them, unless they were used to store allocs. 2783 2784 for (pre_locals_len..f.locals.items.len) |local_i| { 2785 const local_index = @intCast(LocalIndex, local_i); 2786 if (f.allocs.contains(local_index)) { 2787 continue; 2788 } 2789 try freeLocal(f, inst, local_index, 0); 2790 } 2791 } 2792 2793 fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void { 2794 const air_tags = f.air.instructions.items(.tag); 2795 2796 for (body) |inst| { 2797 if (f.liveness.isUnused(inst) and !f.air.mustLower(inst)) { 2798 continue; 2799 } 2800 2801 const result_value = switch (air_tags[inst]) { 2802 // zig fmt: off 2803 .constant => unreachable, // excluded from function bodies 2804 .const_ty => unreachable, // excluded from function bodies 2805 .arg => try airArg(f, inst), 2806 2807 .trap => try airTrap(f.object.writer()), 2808 .breakpoint => try airBreakpoint(f.object.writer()), 2809 .ret_addr => try airRetAddr(f, inst), 2810 .frame_addr => try airFrameAddress(f, inst), 2811 .unreach => try airUnreach(f), 2812 .fence => try airFence(f, inst), 2813 2814 .ptr_add => try airPtrAddSub(f, inst, '+'), 2815 .ptr_sub => try airPtrAddSub(f, inst, '-'), 2816 2817 // TODO use a different strategy for add, sub, mul, div 2818 // that communicates to the optimizer that wrapping is UB. 2819 .add => try airBinOp(f, inst, "+", "add", .none), 2820 .sub => try airBinOp(f, inst, "-", "sub", .none), 2821 .mul => try airBinOp(f, inst, "*", "mul", .none), 2822 2823 .neg => try airFloatNeg(f, inst), 2824 .div_float => try airBinBuiltinCall(f, inst, "div", .none), 2825 2826 .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .none), 2827 .rem => blk: { 2828 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 2829 const lhs_scalar_ty = f.air.typeOf(bin_op.lhs).scalarType(); 2830 // For binary operations @TypeOf(lhs)==@TypeOf(rhs), 2831 // so we only check one. 2832 break :blk if (lhs_scalar_ty.isInt()) 2833 try airBinOp(f, inst, "%", "rem", .none) 2834 else 2835 try airBinFloatOp(f, inst, "fmod"); 2836 }, 2837 .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none), 2838 .mod => try airBinBuiltinCall(f, inst, "mod", .none), 2839 2840 .addwrap => try airBinBuiltinCall(f, inst, "addw", .bits), 2841 .subwrap => try airBinBuiltinCall(f, inst, "subw", .bits), 2842 .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .bits), 2843 2844 .add_sat => try airBinBuiltinCall(f, inst, "adds", .bits), 2845 .sub_sat => try airBinBuiltinCall(f, inst, "subs", .bits), 2846 .mul_sat => try airBinBuiltinCall(f, inst, "muls", .bits), 2847 .shl_sat => try airBinBuiltinCall(f, inst, "shls", .bits), 2848 2849 .sqrt => try airUnFloatOp(f, inst, "sqrt"), 2850 .sin => try airUnFloatOp(f, inst, "sin"), 2851 .cos => try airUnFloatOp(f, inst, "cos"), 2852 .tan => try airUnFloatOp(f, inst, "tan"), 2853 .exp => try airUnFloatOp(f, inst, "exp"), 2854 .exp2 => try airUnFloatOp(f, inst, "exp2"), 2855 .log => try airUnFloatOp(f, inst, "log"), 2856 .log2 => try airUnFloatOp(f, inst, "log2"), 2857 .log10 => try airUnFloatOp(f, inst, "log10"), 2858 .fabs => try airUnFloatOp(f, inst, "fabs"), 2859 .floor => try airUnFloatOp(f, inst, "floor"), 2860 .ceil => try airUnFloatOp(f, inst, "ceil"), 2861 .round => try airUnFloatOp(f, inst, "round"), 2862 .trunc_float => try airUnFloatOp(f, inst, "trunc"), 2863 2864 .mul_add => try airMulAdd(f, inst), 2865 2866 .add_with_overflow => try airOverflow(f, inst, "add", .bits), 2867 .sub_with_overflow => try airOverflow(f, inst, "sub", .bits), 2868 .mul_with_overflow => try airOverflow(f, inst, "mul", .bits), 2869 .shl_with_overflow => try airOverflow(f, inst, "shl", .bits), 2870 2871 .min => try airMinMax(f, inst, '<', "fmin"), 2872 .max => try airMinMax(f, inst, '>', "fmax"), 2873 2874 .slice => try airSlice(f, inst), 2875 2876 .cmp_gt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gt), 2877 .cmp_gte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .gte), 2878 .cmp_lt => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lt), 2879 .cmp_lte => try airCmpOp(f, inst, f.air.instructions.items(.data)[inst].bin_op, .lte), 2880 2881 .cmp_eq => try airEquality(f, inst, .eq), 2882 .cmp_neq => try airEquality(f, inst, .neq), 2883 2884 .cmp_vector => blk: { 2885 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 2886 const extra = f.air.extraData(Air.VectorCmp, ty_pl.payload).data; 2887 break :blk try airCmpOp(f, inst, extra, extra.compareOperator()); 2888 }, 2889 .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst), 2890 2891 // bool_and and bool_or are non-short-circuit operations 2892 .bool_and, .bit_and => try airBinOp(f, inst, "&", "and", .none), 2893 .bool_or, .bit_or => try airBinOp(f, inst, "|", "or", .none), 2894 .xor => try airBinOp(f, inst, "^", "xor", .none), 2895 .shr, .shr_exact => try airBinBuiltinCall(f, inst, "shr", .none), 2896 .shl, => try airBinBuiltinCall(f, inst, "shlw", .bits), 2897 .shl_exact => try airBinOp(f, inst, "<<", "shl", .none), 2898 .not => try airNot (f, inst), 2899 2900 .optional_payload => try airOptionalPayload(f, inst), 2901 .optional_payload_ptr => try airOptionalPayloadPtr(f, inst), 2902 .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst), 2903 .wrap_optional => try airWrapOptional(f, inst), 2904 2905 .is_err => try airIsErr(f, inst, false, "!="), 2906 .is_non_err => try airIsErr(f, inst, false, "=="), 2907 .is_err_ptr => try airIsErr(f, inst, true, "!="), 2908 .is_non_err_ptr => try airIsErr(f, inst, true, "=="), 2909 2910 .is_null => try airIsNull(f, inst, "==", false), 2911 .is_non_null => try airIsNull(f, inst, "!=", false), 2912 .is_null_ptr => try airIsNull(f, inst, "==", true), 2913 .is_non_null_ptr => try airIsNull(f, inst, "!=", true), 2914 2915 .alloc => try airAlloc(f, inst), 2916 .ret_ptr => try airRetPtr(f, inst), 2917 .assembly => try airAsm(f, inst), 2918 .block => try airBlock(f, inst), 2919 .bitcast => try airBitcast(f, inst), 2920 .dbg_stmt => try airDbgStmt(f, inst), 2921 .intcast => try airIntCast(f, inst), 2922 .trunc => try airTrunc(f, inst), 2923 .bool_to_int => try airBoolToInt(f, inst), 2924 .load => try airLoad(f, inst), 2925 .ret => try airRet(f, inst, false), 2926 .ret_load => try airRet(f, inst, true), 2927 .store => try airStore(f, inst, false), 2928 .store_safe => try airStore(f, inst, true), 2929 .loop => try airLoop(f, inst), 2930 .cond_br => try airCondBr(f, inst), 2931 .br => try airBr(f, inst), 2932 .switch_br => try airSwitchBr(f, inst), 2933 .struct_field_ptr => try airStructFieldPtr(f, inst), 2934 .array_to_slice => try airArrayToSlice(f, inst), 2935 .cmpxchg_weak => try airCmpxchg(f, inst, "weak"), 2936 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"), 2937 .atomic_rmw => try airAtomicRmw(f, inst), 2938 .atomic_load => try airAtomicLoad(f, inst), 2939 .memset => try airMemset(f, inst, false), 2940 .memset_safe => try airMemset(f, inst, true), 2941 .memcpy => try airMemcpy(f, inst), 2942 .set_union_tag => try airSetUnionTag(f, inst), 2943 .get_union_tag => try airGetUnionTag(f, inst), 2944 .clz => try airUnBuiltinCall(f, inst, "clz", .bits), 2945 .ctz => try airUnBuiltinCall(f, inst, "ctz", .bits), 2946 .popcount => try airUnBuiltinCall(f, inst, "popcount", .bits), 2947 .byte_swap => try airUnBuiltinCall(f, inst, "byte_swap", .bits), 2948 .bit_reverse => try airUnBuiltinCall(f, inst, "bit_reverse", .bits), 2949 .tag_name => try airTagName(f, inst), 2950 .error_name => try airErrorName(f, inst), 2951 .splat => try airSplat(f, inst), 2952 .select => try airSelect(f, inst), 2953 .shuffle => try airShuffle(f, inst), 2954 .reduce => try airReduce(f, inst), 2955 .aggregate_init => try airAggregateInit(f, inst), 2956 .union_init => try airUnionInit(f, inst), 2957 .prefetch => try airPrefetch(f, inst), 2958 .addrspace_cast => return f.fail("TODO: C backend: implement addrspace_cast", .{}), 2959 2960 .@"try" => try airTry(f, inst), 2961 .try_ptr => try airTryPtr(f, inst), 2962 2963 .dbg_var_ptr, 2964 .dbg_var_val, 2965 => try airDbgVar(f, inst), 2966 2967 .dbg_inline_begin, 2968 .dbg_inline_end, 2969 => try airDbgInline(f, inst), 2970 2971 .dbg_block_begin, 2972 .dbg_block_end, 2973 => .none, 2974 2975 .call => try airCall(f, inst, .auto), 2976 .call_always_tail => .none, 2977 .call_never_tail => try airCall(f, inst, .never_tail), 2978 .call_never_inline => try airCall(f, inst, .never_inline), 2979 2980 .int_to_float, 2981 .float_to_int, 2982 .fptrunc, 2983 .fpext, 2984 => try airFloatCast(f, inst), 2985 2986 .ptrtoint => try airPtrToInt(f, inst), 2987 2988 .atomic_store_unordered => try airAtomicStore(f, inst, toMemoryOrder(.Unordered)), 2989 .atomic_store_monotonic => try airAtomicStore(f, inst, toMemoryOrder(.Monotonic)), 2990 .atomic_store_release => try airAtomicStore(f, inst, toMemoryOrder(.Release)), 2991 .atomic_store_seq_cst => try airAtomicStore(f, inst, toMemoryOrder(.SeqCst)), 2992 2993 .struct_field_ptr_index_0 => try airStructFieldPtrIndex(f, inst, 0), 2994 .struct_field_ptr_index_1 => try airStructFieldPtrIndex(f, inst, 1), 2995 .struct_field_ptr_index_2 => try airStructFieldPtrIndex(f, inst, 2), 2996 .struct_field_ptr_index_3 => try airStructFieldPtrIndex(f, inst, 3), 2997 2998 .field_parent_ptr => try airFieldParentPtr(f, inst), 2999 3000 .struct_field_val => try airStructFieldVal(f, inst), 3001 .slice_ptr => try airSliceField(f, inst, false, "ptr"), 3002 .slice_len => try airSliceField(f, inst, false, "len"), 3003 3004 .ptr_slice_len_ptr => try airSliceField(f, inst, true, "len"), 3005 .ptr_slice_ptr_ptr => try airSliceField(f, inst, true, "ptr"), 3006 3007 .ptr_elem_val => try airPtrElemVal(f, inst), 3008 .ptr_elem_ptr => try airPtrElemPtr(f, inst), 3009 .slice_elem_val => try airSliceElemVal(f, inst), 3010 .slice_elem_ptr => try airSliceElemPtr(f, inst), 3011 .array_elem_val => try airArrayElemVal(f, inst), 3012 3013 .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst, false), 3014 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, true), 3015 .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst), 3016 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst), 3017 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst), 3018 .wrap_errunion_err => try airWrapErrUnionErr(f, inst), 3019 .errunion_payload_ptr_set => try airErrUnionPayloadPtrSet(f, inst), 3020 .err_return_trace => try airErrReturnTrace(f, inst), 3021 .set_err_return_trace => try airSetErrReturnTrace(f, inst), 3022 .save_err_return_trace_index => try airSaveErrReturnTraceIndex(f, inst), 3023 3024 .wasm_memory_size => try airWasmMemorySize(f, inst), 3025 .wasm_memory_grow => try airWasmMemoryGrow(f, inst), 3026 3027 .add_optimized, 3028 .addwrap_optimized, 3029 .sub_optimized, 3030 .subwrap_optimized, 3031 .mul_optimized, 3032 .mulwrap_optimized, 3033 .div_float_optimized, 3034 .div_trunc_optimized, 3035 .div_floor_optimized, 3036 .div_exact_optimized, 3037 .rem_optimized, 3038 .mod_optimized, 3039 .neg_optimized, 3040 .cmp_lt_optimized, 3041 .cmp_lte_optimized, 3042 .cmp_eq_optimized, 3043 .cmp_gte_optimized, 3044 .cmp_gt_optimized, 3045 .cmp_neq_optimized, 3046 .cmp_vector_optimized, 3047 .reduce_optimized, 3048 .float_to_int_optimized, 3049 => return f.fail("TODO implement optimized float mode", .{}), 3050 3051 .is_named_enum_value => return f.fail("TODO: C backend: implement is_named_enum_value", .{}), 3052 .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), 3053 .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), 3054 3055 .c_va_start => try airCVaStart(f, inst), 3056 .c_va_arg => try airCVaArg(f, inst), 3057 .c_va_end => try airCVaEnd(f, inst), 3058 .c_va_copy => try airCVaCopy(f, inst), 3059 3060 .work_item_id, 3061 .work_group_size, 3062 .work_group_id, 3063 => unreachable, 3064 // zig fmt: on 3065 }; 3066 if (result_value == .new_local) { 3067 log.debug("map %{d} to t{d}", .{ inst, result_value.new_local }); 3068 } 3069 try f.value_map.putNoClobber(inst, switch (result_value) { 3070 .none => continue, 3071 .new_local => |i| .{ .local = i }, 3072 else => result_value, 3073 }); 3074 } 3075 } 3076 3077 fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: []const u8) !CValue { 3078 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 3079 3080 const inst_ty = f.air.typeOfIndex(inst); 3081 const operand = try f.resolveInst(ty_op.operand); 3082 try reap(f, inst, &.{ty_op.operand}); 3083 3084 const writer = f.object.writer(); 3085 const local = try f.allocLocal(inst, inst_ty); 3086 const a = try Assignment.start(f, writer, inst_ty); 3087 try f.writeCValue(writer, local, .Other); 3088 try a.assign(f, writer); 3089 if (is_ptr) { 3090 try writer.writeByte('&'); 3091 try f.writeCValueDerefMember(writer, operand, .{ .identifier = field_name }); 3092 } else try f.writeCValueMember(writer, operand, .{ .identifier = field_name }); 3093 try a.end(f, writer); 3094 return local; 3095 } 3096 3097 fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { 3098 const inst_ty = f.air.typeOfIndex(inst); 3099 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 3100 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { 3101 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3102 return .none; 3103 } 3104 3105 const ptr = try f.resolveInst(bin_op.lhs); 3106 const index = try f.resolveInst(bin_op.rhs); 3107 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3108 3109 const writer = f.object.writer(); 3110 const local = try f.allocLocal(inst, inst_ty); 3111 const a = try Assignment.start(f, writer, inst_ty); 3112 try f.writeCValue(writer, local, .Other); 3113 try a.assign(f, writer); 3114 try f.writeCValue(writer, ptr, .Other); 3115 try writer.writeByte('['); 3116 try f.writeCValue(writer, index, .Other); 3117 try writer.writeByte(']'); 3118 try a.end(f, writer); 3119 return local; 3120 } 3121 3122 fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { 3123 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 3124 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; 3125 3126 const inst_ty = f.air.typeOfIndex(inst); 3127 const ptr_ty = f.air.typeOf(bin_op.lhs); 3128 const elem_ty = ptr_ty.childType(); 3129 const elem_has_bits = elem_ty.hasRuntimeBitsIgnoreComptime(); 3130 3131 const ptr = try f.resolveInst(bin_op.lhs); 3132 const index = try f.resolveInst(bin_op.rhs); 3133 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3134 3135 const writer = f.object.writer(); 3136 const local = try f.allocLocal(inst, inst_ty); 3137 const a = try Assignment.start(f, writer, inst_ty); 3138 try f.writeCValue(writer, local, .Other); 3139 try a.assign(f, writer); 3140 try writer.writeByte('('); 3141 try f.renderType(writer, inst_ty); 3142 try writer.writeByte(')'); 3143 if (elem_has_bits) try writer.writeByte('&'); 3144 if (elem_has_bits and ptr_ty.ptrSize() == .One) { 3145 // It's a pointer to an array, so we need to de-reference. 3146 try f.writeCValueDeref(writer, ptr); 3147 } else try f.writeCValue(writer, ptr, .Other); 3148 if (elem_has_bits) { 3149 try writer.writeByte('['); 3150 try f.writeCValue(writer, index, .Other); 3151 try writer.writeByte(']'); 3152 } 3153 try a.end(f, writer); 3154 return local; 3155 } 3156 3157 fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { 3158 const inst_ty = f.air.typeOfIndex(inst); 3159 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 3160 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { 3161 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3162 return .none; 3163 } 3164 3165 const slice = try f.resolveInst(bin_op.lhs); 3166 const index = try f.resolveInst(bin_op.rhs); 3167 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3168 3169 const writer = f.object.writer(); 3170 const local = try f.allocLocal(inst, inst_ty); 3171 const a = try Assignment.start(f, writer, inst_ty); 3172 try f.writeCValue(writer, local, .Other); 3173 try a.assign(f, writer); 3174 try f.writeCValueMember(writer, slice, .{ .identifier = "ptr" }); 3175 try writer.writeByte('['); 3176 try f.writeCValue(writer, index, .Other); 3177 try writer.writeByte(']'); 3178 try a.end(f, writer); 3179 return local; 3180 } 3181 3182 fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { 3183 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 3184 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; 3185 3186 const inst_ty = f.air.typeOfIndex(inst); 3187 const slice_ty = f.air.typeOf(bin_op.lhs); 3188 const elem_ty = slice_ty.elemType2(); 3189 const elem_has_bits = elem_ty.hasRuntimeBitsIgnoreComptime(); 3190 3191 const slice = try f.resolveInst(bin_op.lhs); 3192 const index = try f.resolveInst(bin_op.rhs); 3193 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3194 3195 const writer = f.object.writer(); 3196 const local = try f.allocLocal(inst, inst_ty); 3197 const a = try Assignment.start(f, writer, inst_ty); 3198 try f.writeCValue(writer, local, .Other); 3199 try a.assign(f, writer); 3200 if (elem_has_bits) try writer.writeByte('&'); 3201 try f.writeCValueMember(writer, slice, .{ .identifier = "ptr" }); 3202 if (elem_has_bits) { 3203 try writer.writeByte('['); 3204 try f.writeCValue(writer, index, .Other); 3205 try writer.writeByte(']'); 3206 } 3207 try a.end(f, writer); 3208 return local; 3209 } 3210 3211 fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { 3212 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 3213 const inst_ty = f.air.typeOfIndex(inst); 3214 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { 3215 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3216 return .none; 3217 } 3218 3219 const array = try f.resolveInst(bin_op.lhs); 3220 const index = try f.resolveInst(bin_op.rhs); 3221 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3222 3223 const writer = f.object.writer(); 3224 const local = try f.allocLocal(inst, inst_ty); 3225 const a = try Assignment.start(f, writer, inst_ty); 3226 try f.writeCValue(writer, local, .Other); 3227 try a.assign(f, writer); 3228 try f.writeCValue(writer, array, .Other); 3229 try writer.writeByte('['); 3230 try f.writeCValue(writer, index, .Other); 3231 try writer.writeByte(']'); 3232 try a.end(f, writer); 3233 return local; 3234 } 3235 3236 fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { 3237 const inst_ty = f.air.typeOfIndex(inst); 3238 const elem_type = inst_ty.elemType(); 3239 if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty }; 3240 3241 const target = f.object.dg.module.getTarget(); 3242 const local = try f.allocLocalValue( 3243 elem_type, 3244 inst_ty.ptrAlignment(target), 3245 ); 3246 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); 3247 const gpa = f.object.dg.module.gpa; 3248 try f.allocs.put(gpa, local.new_local, true); 3249 return .{ .local_ref = local.new_local }; 3250 } 3251 3252 fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { 3253 const inst_ty = f.air.typeOfIndex(inst); 3254 const elem_ty = inst_ty.elemType(); 3255 if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) return .{ .undef = inst_ty }; 3256 3257 const target = f.object.dg.module.getTarget(); 3258 const local = try f.allocLocalValue( 3259 elem_ty, 3260 inst_ty.ptrAlignment(target), 3261 ); 3262 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); 3263 const gpa = f.object.dg.module.gpa; 3264 try f.allocs.put(gpa, local.new_local, true); 3265 return .{ .local_ref = local.new_local }; 3266 } 3267 3268 fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { 3269 const inst_ty = f.air.typeOfIndex(inst); 3270 const inst_cty = try f.typeToIndex(inst_ty, .parameter); 3271 3272 const i = f.next_arg_index; 3273 f.next_arg_index += 1; 3274 const result: CValue = if (inst_cty != try f.typeToIndex(inst_ty, .complete)) 3275 .{ .arg_array = i } 3276 else 3277 .{ .arg = i }; 3278 3279 if (f.liveness.isUnused(inst)) { 3280 const writer = f.object.writer(); 3281 try writer.writeByte('('); 3282 try f.renderType(writer, Type.void); 3283 try writer.writeByte(')'); 3284 try f.writeCValue(writer, result, .Other); 3285 try writer.writeAll(";\n"); 3286 return .none; 3287 } 3288 3289 return result; 3290 } 3291 3292 fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { 3293 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 3294 3295 const ptr_ty = f.air.typeOf(ty_op.operand); 3296 const ptr_scalar_ty = ptr_ty.scalarType(); 3297 const ptr_info = ptr_scalar_ty.ptrInfo().data; 3298 const src_ty = ptr_info.pointee_type; 3299 3300 if (!src_ty.hasRuntimeBitsIgnoreComptime()) { 3301 try reap(f, inst, &.{ty_op.operand}); 3302 return .none; 3303 } 3304 3305 const operand = try f.resolveInst(ty_op.operand); 3306 3307 try reap(f, inst, &.{ty_op.operand}); 3308 3309 const target = f.object.dg.module.getTarget(); 3310 const is_aligned = ptr_info.@"align" == 0 or ptr_info.@"align" >= src_ty.abiAlignment(target); 3311 const is_array = lowersToArray(src_ty, target); 3312 const need_memcpy = !is_aligned or is_array; 3313 3314 const writer = f.object.writer(); 3315 const local = try f.allocLocal(inst, src_ty); 3316 const v = try Vectorize.start(f, inst, writer, ptr_ty); 3317 3318 if (need_memcpy) { 3319 try writer.writeAll("memcpy("); 3320 if (!is_array) try writer.writeByte('&'); 3321 try f.writeCValue(writer, local, .Other); 3322 try v.elem(f, writer); 3323 try writer.writeAll(", (const char *)"); 3324 try f.writeCValue(writer, operand, .Other); 3325 try v.elem(f, writer); 3326 try writer.writeAll(", sizeof("); 3327 try f.renderType(writer, src_ty); 3328 try writer.writeAll("))"); 3329 } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) { 3330 var host_pl = Type.Payload.Bits{ 3331 .base = .{ .tag = .int_unsigned }, 3332 .data = ptr_info.host_size * 8, 3333 }; 3334 const host_ty = Type.initPayload(&host_pl.base); 3335 3336 var bit_offset_ty_pl = Type.Payload.Bits{ 3337 .base = .{ .tag = .int_unsigned }, 3338 .data = Type.smallestUnsignedBits(host_pl.data - 1), 3339 }; 3340 const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); 3341 3342 var bit_offset_val_pl: Value.Payload.U64 = .{ 3343 .base = .{ .tag = .int_u64 }, 3344 .data = ptr_info.bit_offset, 3345 }; 3346 const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); 3347 3348 var field_pl = Type.Payload.Bits{ 3349 .base = .{ .tag = .int_unsigned }, 3350 .data = @intCast(u16, src_ty.bitSize(target)), 3351 }; 3352 const field_ty = Type.initPayload(&field_pl.base); 3353 3354 try f.writeCValue(writer, local, .Other); 3355 try v.elem(f, writer); 3356 try writer.writeAll(" = ("); 3357 try f.renderType(writer, src_ty); 3358 try writer.writeAll(")zig_wrap_"); 3359 try f.object.dg.renderTypeForBuiltinFnName(writer, field_ty); 3360 try writer.writeAll("(("); 3361 try f.renderType(writer, field_ty); 3362 try writer.writeByte(')'); 3363 const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; 3364 if (cant_cast) { 3365 if (field_ty.bitSize(target) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{}); 3366 try writer.writeAll("zig_lo_"); 3367 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); 3368 try writer.writeByte('('); 3369 } 3370 try writer.writeAll("zig_shr_"); 3371 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); 3372 try writer.writeByte('('); 3373 try f.writeCValueDeref(writer, operand); 3374 try v.elem(f, writer); 3375 try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); 3376 if (cant_cast) try writer.writeByte(')'); 3377 try f.object.dg.renderBuiltinInfo(writer, field_ty, .bits); 3378 try writer.writeByte(')'); 3379 } else { 3380 try f.writeCValue(writer, local, .Other); 3381 try v.elem(f, writer); 3382 try writer.writeAll(" = "); 3383 try f.writeCValueDeref(writer, operand); 3384 try v.elem(f, writer); 3385 } 3386 try writer.writeAll(";\n"); 3387 try v.end(f, inst, writer); 3388 3389 return local; 3390 } 3391 3392 fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { 3393 const un_op = f.air.instructions.items(.data)[inst].un_op; 3394 const writer = f.object.writer(); 3395 const target = f.object.dg.module.getTarget(); 3396 const op_inst = Air.refToIndex(un_op); 3397 const op_ty = f.air.typeOf(un_op); 3398 const ret_ty = if (is_ptr) op_ty.childType() else op_ty; 3399 var lowered_ret_buf: LowerFnRetTyBuffer = undefined; 3400 const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); 3401 3402 if (op_inst != null and f.air.instructions.items(.tag)[op_inst.?] == .call_always_tail) { 3403 try reap(f, inst, &.{un_op}); 3404 _ = try airCall(f, op_inst.?, .always_tail); 3405 } else if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { 3406 const operand = try f.resolveInst(un_op); 3407 try reap(f, inst, &.{un_op}); 3408 var deref = is_ptr; 3409 const is_array = lowersToArray(ret_ty, target); 3410 const ret_val = if (is_array) ret_val: { 3411 const array_local = try f.allocLocal(inst, lowered_ret_ty); 3412 try writer.writeAll("memcpy("); 3413 try f.writeCValueMember(writer, array_local, .{ .identifier = "array" }); 3414 try writer.writeAll(", "); 3415 if (deref) 3416 try f.writeCValueDeref(writer, operand) 3417 else 3418 try f.writeCValue(writer, operand, .FunctionArgument); 3419 deref = false; 3420 try writer.writeAll(", sizeof("); 3421 try f.renderType(writer, ret_ty); 3422 try writer.writeAll("));\n"); 3423 break :ret_val array_local; 3424 } else operand; 3425 3426 try writer.writeAll("return "); 3427 if (deref) 3428 try f.writeCValueDeref(writer, ret_val) 3429 else 3430 try f.writeCValue(writer, ret_val, .Other); 3431 try writer.writeAll(";\n"); 3432 if (is_array) { 3433 try freeLocal(f, inst, ret_val.new_local, 0); 3434 } 3435 } else { 3436 try reap(f, inst, &.{un_op}); 3437 // Not even allowed to return void in a naked function. 3438 if (if (f.object.dg.decl) |decl| decl.ty.fnCallingConvention() != .Naked else true) 3439 try writer.writeAll("return;\n"); 3440 } 3441 return .none; 3442 } 3443 3444 fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { 3445 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 3446 3447 const operand = try f.resolveInst(ty_op.operand); 3448 try reap(f, inst, &.{ty_op.operand}); 3449 3450 const inst_ty = f.air.typeOfIndex(inst); 3451 const inst_scalar_ty = inst_ty.scalarType(); 3452 const operand_ty = f.air.typeOf(ty_op.operand); 3453 const scalar_ty = operand_ty.scalarType(); 3454 3455 const writer = f.object.writer(); 3456 const local = try f.allocLocal(inst, inst_ty); 3457 const v = try Vectorize.start(f, inst, writer, operand_ty); 3458 const a = try Assignment.start(f, writer, scalar_ty); 3459 try f.writeCValue(writer, local, .Other); 3460 try v.elem(f, writer); 3461 try a.assign(f, writer); 3462 try f.renderIntCast(writer, inst_scalar_ty, operand, v, scalar_ty, .Other); 3463 try a.end(f, writer); 3464 try v.end(f, inst, writer); 3465 3466 return local; 3467 } 3468 3469 fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { 3470 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 3471 3472 const operand = try f.resolveInst(ty_op.operand); 3473 try reap(f, inst, &.{ty_op.operand}); 3474 const inst_ty = f.air.typeOfIndex(inst); 3475 const inst_scalar_ty = inst_ty.scalarType(); 3476 const target = f.object.dg.module.getTarget(); 3477 const dest_int_info = inst_scalar_ty.intInfo(target); 3478 const dest_bits = dest_int_info.bits; 3479 const dest_c_bits = toCIntBits(dest_int_info.bits) orelse 3480 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); 3481 const operand_ty = f.air.typeOf(ty_op.operand); 3482 const scalar_ty = operand_ty.scalarType(); 3483 const scalar_int_info = scalar_ty.intInfo(target); 3484 3485 const writer = f.object.writer(); 3486 const local = try f.allocLocal(inst, inst_ty); 3487 const v = try Vectorize.start(f, inst, writer, operand_ty); 3488 3489 try f.writeCValue(writer, local, .Other); 3490 try v.elem(f, writer); 3491 try writer.writeAll(" = "); 3492 3493 if (dest_c_bits < 64) { 3494 try writer.writeByte('('); 3495 try f.renderType(writer, inst_scalar_ty); 3496 try writer.writeByte(')'); 3497 } 3498 3499 const needs_lo = scalar_int_info.bits > 64 and dest_bits <= 64; 3500 if (needs_lo) { 3501 try writer.writeAll("zig_lo_"); 3502 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 3503 try writer.writeByte('('); 3504 } 3505 3506 if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { 3507 try f.writeCValue(writer, operand, .Other); 3508 try v.elem(f, writer); 3509 } else switch (dest_int_info.signedness) { 3510 .unsigned => { 3511 var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); 3512 defer arena.deinit(); 3513 3514 const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 }; 3515 var stack align(@alignOf(ExpectedContents)) = 3516 std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); 3517 3518 const mask_val = try inst_scalar_ty.maxInt(stack.get(), target); 3519 try writer.writeAll("zig_and_"); 3520 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 3521 try writer.writeByte('('); 3522 try f.writeCValue(writer, operand, .FunctionArgument); 3523 try v.elem(f, writer); 3524 try writer.print(", {x})", .{try f.fmtIntLiteral(scalar_ty, mask_val)}); 3525 }, 3526 .signed => { 3527 const c_bits = toCIntBits(scalar_int_info.bits) orelse 3528 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); 3529 var shift_pl = Value.Payload.U64{ 3530 .base = .{ .tag = .int_u64 }, 3531 .data = c_bits - dest_bits, 3532 }; 3533 const shift_val = Value.initPayload(&shift_pl.base); 3534 3535 try writer.writeAll("zig_shr_"); 3536 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 3537 if (c_bits == 128) { 3538 try writer.print("(zig_bitCast_i{d}(", .{c_bits}); 3539 } else { 3540 try writer.print("((int{d}_t)", .{c_bits}); 3541 } 3542 try writer.print("zig_shl_u{d}(", .{c_bits}); 3543 if (c_bits == 128) { 3544 try writer.print("zig_bitCast_u{d}(", .{c_bits}); 3545 } else { 3546 try writer.print("(uint{d}_t)", .{c_bits}); 3547 } 3548 try f.writeCValue(writer, operand, .FunctionArgument); 3549 try v.elem(f, writer); 3550 if (c_bits == 128) try writer.writeByte(')'); 3551 try writer.print(", {})", .{try f.fmtIntLiteral(Type.u8, shift_val)}); 3552 if (c_bits == 128) try writer.writeByte(')'); 3553 try writer.print(", {})", .{try f.fmtIntLiteral(Type.u8, shift_val)}); 3554 }, 3555 } 3556 3557 if (needs_lo) try writer.writeByte(')'); 3558 try writer.writeAll(";\n"); 3559 try v.end(f, inst, writer); 3560 3561 return local; 3562 } 3563 3564 fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { 3565 const un_op = f.air.instructions.items(.data)[inst].un_op; 3566 const operand = try f.resolveInst(un_op); 3567 try reap(f, inst, &.{un_op}); 3568 const writer = f.object.writer(); 3569 const inst_ty = f.air.typeOfIndex(inst); 3570 const local = try f.allocLocal(inst, inst_ty); 3571 const a = try Assignment.start(f, writer, inst_ty); 3572 try f.writeCValue(writer, local, .Other); 3573 try a.assign(f, writer); 3574 try f.writeCValue(writer, operand, .Other); 3575 try a.end(f, writer); 3576 return local; 3577 } 3578 3579 fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { 3580 // *a = b; 3581 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 3582 3583 const ptr_ty = f.air.typeOf(bin_op.lhs); 3584 const ptr_scalar_ty = ptr_ty.scalarType(); 3585 const ptr_info = ptr_scalar_ty.ptrInfo().data; 3586 3587 const ptr_val = try f.resolveInst(bin_op.lhs); 3588 const src_ty = f.air.typeOf(bin_op.rhs); 3589 3590 const val_is_undef = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; 3591 3592 if (val_is_undef) { 3593 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3594 if (safety and ptr_info.host_size == 0) { 3595 const writer = f.object.writer(); 3596 try writer.writeAll("memset("); 3597 try f.writeCValue(writer, ptr_val, .FunctionArgument); 3598 try writer.writeAll(", 0xaa, sizeof("); 3599 try f.renderType(writer, ptr_info.pointee_type); 3600 try writer.writeAll("));\n"); 3601 } 3602 return .none; 3603 } 3604 3605 const target = f.object.dg.module.getTarget(); 3606 const is_aligned = ptr_info.@"align" == 0 or 3607 ptr_info.@"align" >= ptr_info.pointee_type.abiAlignment(target); 3608 const is_array = lowersToArray(ptr_info.pointee_type, target); 3609 const need_memcpy = !is_aligned or is_array; 3610 3611 const src_val = try f.resolveInst(bin_op.rhs); 3612 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3613 3614 const writer = f.object.writer(); 3615 const v = try Vectorize.start(f, inst, writer, ptr_ty); 3616 3617 if (need_memcpy) { 3618 // For this memcpy to safely work we need the rhs to have the same 3619 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). 3620 assert(src_ty.eql(ptr_info.pointee_type, f.object.dg.module)); 3621 3622 // If the source is a constant, writeCValue will emit a brace initialization 3623 // so work around this by initializing into new local. 3624 // TODO this should be done by manually initializing elements of the dest array 3625 const array_src = if (src_val == .constant) blk: { 3626 const new_local = try f.allocLocal(inst, src_ty); 3627 try f.writeCValue(writer, new_local, .Other); 3628 try writer.writeAll(" = "); 3629 try f.writeCValue(writer, src_val, .Initializer); 3630 try writer.writeAll(";\n"); 3631 3632 break :blk new_local; 3633 } else src_val; 3634 3635 try writer.writeAll("memcpy((char *)"); 3636 try f.writeCValue(writer, ptr_val, .FunctionArgument); 3637 try v.elem(f, writer); 3638 try writer.writeAll(", "); 3639 if (!is_array) try writer.writeByte('&'); 3640 try f.writeCValue(writer, array_src, .FunctionArgument); 3641 try v.elem(f, writer); 3642 try writer.writeAll(", sizeof("); 3643 try f.renderType(writer, src_ty); 3644 try writer.writeAll("))"); 3645 if (src_val == .constant) { 3646 try freeLocal(f, inst, array_src.new_local, 0); 3647 } 3648 } else if (ptr_info.host_size > 0 and ptr_info.vector_index == .none) { 3649 const host_bits = ptr_info.host_size * 8; 3650 var host_pl = Type.Payload.Bits{ .base = .{ .tag = .int_unsigned }, .data = host_bits }; 3651 const host_ty = Type.initPayload(&host_pl.base); 3652 3653 var bit_offset_ty_pl = Type.Payload.Bits{ 3654 .base = .{ .tag = .int_unsigned }, 3655 .data = Type.smallestUnsignedBits(host_bits - 1), 3656 }; 3657 const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); 3658 3659 var bit_offset_val_pl: Value.Payload.U64 = .{ 3660 .base = .{ .tag = .int_u64 }, 3661 .data = ptr_info.bit_offset, 3662 }; 3663 const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); 3664 3665 const src_bits = src_ty.bitSize(target); 3666 3667 const ExpectedContents = [BigInt.Managed.default_capacity]BigIntLimb; 3668 var stack align(@alignOf(ExpectedContents)) = 3669 std.heap.stackFallback(@sizeOf(ExpectedContents), f.object.dg.gpa); 3670 3671 var mask = try BigInt.Managed.initCapacity(stack.get(), BigInt.calcTwosCompLimbCount(host_bits)); 3672 defer mask.deinit(); 3673 3674 try mask.setTwosCompIntLimit(.max, .unsigned, @intCast(usize, src_bits)); 3675 try mask.shiftLeft(&mask, ptr_info.bit_offset); 3676 try mask.bitNotWrap(&mask, .unsigned, host_bits); 3677 3678 var mask_pl = Value.Payload.BigInt{ 3679 .base = .{ .tag = .int_big_positive }, 3680 .data = mask.limbs[0..mask.len()], 3681 }; 3682 const mask_val = Value.initPayload(&mask_pl.base); 3683 3684 try f.writeCValueDeref(writer, ptr_val); 3685 try v.elem(f, writer); 3686 try writer.writeAll(" = zig_or_"); 3687 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); 3688 try writer.writeAll("(zig_and_"); 3689 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); 3690 try writer.writeByte('('); 3691 try f.writeCValueDeref(writer, ptr_val); 3692 try v.elem(f, writer); 3693 try writer.print(", {x}), zig_shl_", .{try f.fmtIntLiteral(host_ty, mask_val)}); 3694 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); 3695 try writer.writeByte('('); 3696 const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; 3697 if (cant_cast) { 3698 if (src_ty.bitSize(target) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{}); 3699 try writer.writeAll("zig_make_"); 3700 try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); 3701 try writer.writeAll("(0, "); 3702 } else { 3703 try writer.writeByte('('); 3704 try f.renderType(writer, host_ty); 3705 try writer.writeByte(')'); 3706 } 3707 3708 if (src_ty.isPtrAtRuntime()) { 3709 try writer.writeByte('('); 3710 try f.renderType(writer, Type.usize); 3711 try writer.writeByte(')'); 3712 } 3713 try f.writeCValue(writer, src_val, .Other); 3714 try v.elem(f, writer); 3715 if (cant_cast) try writer.writeByte(')'); 3716 try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)}); 3717 } else { 3718 try f.writeCValueDeref(writer, ptr_val); 3719 try v.elem(f, writer); 3720 try writer.writeAll(" = "); 3721 try f.writeCValue(writer, src_val, .Other); 3722 try v.elem(f, writer); 3723 } 3724 try writer.writeAll(";\n"); 3725 try v.end(f, inst, writer); 3726 3727 return .none; 3728 } 3729 3730 fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue { 3731 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 3732 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; 3733 3734 const lhs = try f.resolveInst(bin_op.lhs); 3735 const rhs = try f.resolveInst(bin_op.rhs); 3736 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3737 3738 const inst_ty = f.air.typeOfIndex(inst); 3739 const operand_ty = f.air.typeOf(bin_op.lhs); 3740 const scalar_ty = operand_ty.scalarType(); 3741 3742 const w = f.object.writer(); 3743 const local = try f.allocLocal(inst, inst_ty); 3744 const v = try Vectorize.start(f, inst, w, operand_ty); 3745 try f.writeCValueMember(w, local, .{ .field = 1 }); 3746 try v.elem(f, w); 3747 try w.writeAll(" = zig_"); 3748 try w.writeAll(operation); 3749 try w.writeAll("o_"); 3750 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty); 3751 try w.writeAll("(&"); 3752 try f.writeCValueMember(w, local, .{ .field = 0 }); 3753 try v.elem(f, w); 3754 try w.writeAll(", "); 3755 try f.writeCValue(w, lhs, .FunctionArgument); 3756 try v.elem(f, w); 3757 try w.writeAll(", "); 3758 try f.writeCValue(w, rhs, .FunctionArgument); 3759 try v.elem(f, w); 3760 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info); 3761 try w.writeAll(");\n"); 3762 try v.end(f, inst, w); 3763 3764 return local; 3765 } 3766 3767 fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { 3768 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 3769 const operand_ty = f.air.typeOf(ty_op.operand); 3770 const scalar_ty = operand_ty.scalarType(); 3771 if (scalar_ty.tag() != .bool) return try airUnBuiltinCall(f, inst, "not", .bits); 3772 3773 const op = try f.resolveInst(ty_op.operand); 3774 try reap(f, inst, &.{ty_op.operand}); 3775 3776 const inst_ty = f.air.typeOfIndex(inst); 3777 3778 const writer = f.object.writer(); 3779 const local = try f.allocLocal(inst, inst_ty); 3780 const v = try Vectorize.start(f, inst, writer, operand_ty); 3781 try f.writeCValue(writer, local, .Other); 3782 try v.elem(f, writer); 3783 try writer.writeAll(" = "); 3784 try writer.writeByte('!'); 3785 try f.writeCValue(writer, op, .Other); 3786 try v.elem(f, writer); 3787 try writer.writeAll(";\n"); 3788 try v.end(f, inst, writer); 3789 3790 return local; 3791 } 3792 3793 fn airBinOp( 3794 f: *Function, 3795 inst: Air.Inst.Index, 3796 operator: []const u8, 3797 operation: []const u8, 3798 info: BuiltinInfo, 3799 ) !CValue { 3800 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 3801 const operand_ty = f.air.typeOf(bin_op.lhs); 3802 const scalar_ty = operand_ty.scalarType(); 3803 const target = f.object.dg.module.getTarget(); 3804 if ((scalar_ty.isInt() and scalar_ty.bitSize(target) > 64) or scalar_ty.isRuntimeFloat()) 3805 return try airBinBuiltinCall(f, inst, operation, info); 3806 3807 const lhs = try f.resolveInst(bin_op.lhs); 3808 const rhs = try f.resolveInst(bin_op.rhs); 3809 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3810 3811 const inst_ty = f.air.typeOfIndex(inst); 3812 3813 const writer = f.object.writer(); 3814 const local = try f.allocLocal(inst, inst_ty); 3815 const v = try Vectorize.start(f, inst, writer, operand_ty); 3816 try f.writeCValue(writer, local, .Other); 3817 try v.elem(f, writer); 3818 try writer.writeAll(" = "); 3819 try f.writeCValue(writer, lhs, .Other); 3820 try v.elem(f, writer); 3821 try writer.writeByte(' '); 3822 try writer.writeAll(operator); 3823 try writer.writeByte(' '); 3824 try f.writeCValue(writer, rhs, .Other); 3825 try v.elem(f, writer); 3826 try writer.writeAll(";\n"); 3827 try v.end(f, inst, writer); 3828 3829 return local; 3830 } 3831 3832 fn airCmpOp( 3833 f: *Function, 3834 inst: Air.Inst.Index, 3835 data: anytype, 3836 operator: std.math.CompareOperator, 3837 ) !CValue { 3838 const lhs_ty = f.air.typeOf(data.lhs); 3839 const scalar_ty = lhs_ty.scalarType(); 3840 3841 const target = f.object.dg.module.getTarget(); 3842 const scalar_bits = scalar_ty.bitSize(target); 3843 if (scalar_ty.isInt() and scalar_bits > 64) 3844 return airCmpBuiltinCall( 3845 f, 3846 inst, 3847 data, 3848 operator, 3849 .cmp, 3850 if (scalar_bits > 128) .bits else .none, 3851 ); 3852 if (scalar_ty.isRuntimeFloat()) 3853 return airCmpBuiltinCall(f, inst, data, operator, .operator, .none); 3854 3855 const inst_ty = f.air.typeOfIndex(inst); 3856 const lhs = try f.resolveInst(data.lhs); 3857 const rhs = try f.resolveInst(data.rhs); 3858 try reap(f, inst, &.{ data.lhs, data.rhs }); 3859 3860 const rhs_ty = f.air.typeOf(data.rhs); 3861 const need_cast = lhs_ty.isSinglePointer() or rhs_ty.isSinglePointer(); 3862 const writer = f.object.writer(); 3863 const local = try f.allocLocal(inst, inst_ty); 3864 const v = try Vectorize.start(f, inst, writer, lhs_ty); 3865 try f.writeCValue(writer, local, .Other); 3866 try v.elem(f, writer); 3867 try writer.writeAll(" = "); 3868 if (need_cast) try writer.writeAll("(void*)"); 3869 try f.writeCValue(writer, lhs, .Other); 3870 try v.elem(f, writer); 3871 try writer.writeByte(' '); 3872 try writer.writeAll(compareOperatorC(operator)); 3873 try writer.writeByte(' '); 3874 if (need_cast) try writer.writeAll("(void*)"); 3875 try f.writeCValue(writer, rhs, .Other); 3876 try v.elem(f, writer); 3877 try writer.writeAll(";\n"); 3878 try v.end(f, inst, writer); 3879 3880 return local; 3881 } 3882 3883 fn airEquality( 3884 f: *Function, 3885 inst: Air.Inst.Index, 3886 operator: std.math.CompareOperator, 3887 ) !CValue { 3888 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 3889 3890 const operand_ty = f.air.typeOf(bin_op.lhs); 3891 const target = f.object.dg.module.getTarget(); 3892 const operand_bits = operand_ty.bitSize(target); 3893 if (operand_ty.isInt() and operand_bits > 64) 3894 return airCmpBuiltinCall( 3895 f, 3896 inst, 3897 bin_op, 3898 operator, 3899 .cmp, 3900 if (operand_bits > 128) .bits else .none, 3901 ); 3902 if (operand_ty.isRuntimeFloat()) 3903 return airCmpBuiltinCall(f, inst, bin_op, operator, .operator, .none); 3904 3905 const lhs = try f.resolveInst(bin_op.lhs); 3906 const rhs = try f.resolveInst(bin_op.rhs); 3907 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3908 3909 const writer = f.object.writer(); 3910 const inst_ty = f.air.typeOfIndex(inst); 3911 const local = try f.allocLocal(inst, inst_ty); 3912 try f.writeCValue(writer, local, .Other); 3913 try writer.writeAll(" = "); 3914 3915 if (operand_ty.zigTypeTag() == .Optional and !operand_ty.optionalReprIsPayload()) { 3916 // (A && B) || (C && (A == B)) 3917 // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload 3918 3919 switch (operator) { 3920 .eq => {}, 3921 .neq => try writer.writeByte('!'), 3922 else => unreachable, 3923 } 3924 try writer.writeAll("(("); 3925 try f.writeCValue(writer, lhs, .Other); 3926 try writer.writeAll(".is_null && "); 3927 try f.writeCValue(writer, rhs, .Other); 3928 try writer.writeAll(".is_null) || ("); 3929 try f.writeCValue(writer, lhs, .Other); 3930 try writer.writeAll(".payload == "); 3931 try f.writeCValue(writer, rhs, .Other); 3932 try writer.writeAll(".payload && "); 3933 try f.writeCValue(writer, lhs, .Other); 3934 try writer.writeAll(".is_null == "); 3935 try f.writeCValue(writer, rhs, .Other); 3936 try writer.writeAll(".is_null));\n"); 3937 3938 return local; 3939 } 3940 3941 try f.writeCValue(writer, lhs, .Other); 3942 try writer.writeByte(' '); 3943 try writer.writeAll(compareOperatorC(operator)); 3944 try writer.writeByte(' '); 3945 try f.writeCValue(writer, rhs, .Other); 3946 try writer.writeAll(";\n"); 3947 3948 return local; 3949 } 3950 3951 fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { 3952 const un_op = f.air.instructions.items(.data)[inst].un_op; 3953 3954 const inst_ty = f.air.typeOfIndex(inst); 3955 const operand = try f.resolveInst(un_op); 3956 try reap(f, inst, &.{un_op}); 3957 3958 const writer = f.object.writer(); 3959 const local = try f.allocLocal(inst, inst_ty); 3960 try f.writeCValue(writer, local, .Other); 3961 try writer.writeAll(" = "); 3962 try f.writeCValue(writer, operand, .Other); 3963 try writer.print(" < sizeof({ }) / sizeof(*{0 });\n", .{fmtIdent("zig_errorName")}); 3964 return local; 3965 } 3966 3967 fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { 3968 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 3969 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; 3970 3971 const lhs = try f.resolveInst(bin_op.lhs); 3972 const rhs = try f.resolveInst(bin_op.rhs); 3973 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 3974 3975 const inst_ty = f.air.typeOfIndex(inst); 3976 const inst_scalar_ty = inst_ty.scalarType(); 3977 const elem_ty = inst_scalar_ty.elemType2(); 3978 3979 const local = try f.allocLocal(inst, inst_ty); 3980 const writer = f.object.writer(); 3981 const v = try Vectorize.start(f, inst, writer, inst_ty); 3982 try f.writeCValue(writer, local, .Other); 3983 try v.elem(f, writer); 3984 try writer.writeAll(" = "); 3985 3986 if (elem_ty.hasRuntimeBitsIgnoreComptime()) { 3987 // We must convert to and from integer types to prevent UB if the operation 3988 // results in a NULL pointer, or if LHS is NULL. The operation is only UB 3989 // if the result is NULL and then dereferenced. 3990 try writer.writeByte('('); 3991 try f.renderType(writer, inst_scalar_ty); 3992 try writer.writeAll(")(((uintptr_t)"); 3993 try f.writeCValue(writer, lhs, .Other); 3994 try v.elem(f, writer); 3995 try writer.writeAll(") "); 3996 try writer.writeByte(operator); 3997 try writer.writeAll(" ("); 3998 try f.writeCValue(writer, rhs, .Other); 3999 try v.elem(f, writer); 4000 try writer.writeAll("*sizeof("); 4001 try f.renderType(writer, elem_ty); 4002 try writer.writeAll(")))"); 4003 } else { 4004 try f.writeCValue(writer, lhs, .Other); 4005 try v.elem(f, writer); 4006 } 4007 4008 try writer.writeAll(";\n"); 4009 try v.end(f, inst, writer); 4010 4011 return local; 4012 } 4013 4014 fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue { 4015 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 4016 4017 const inst_ty = f.air.typeOfIndex(inst); 4018 const inst_scalar_ty = inst_ty.scalarType(); 4019 4020 const target = f.object.dg.module.getTarget(); 4021 if (inst_scalar_ty.isInt() and inst_scalar_ty.bitSize(target) > 64) 4022 return try airBinBuiltinCall(f, inst, operation[1..], .none); 4023 if (inst_scalar_ty.isRuntimeFloat()) 4024 return try airBinFloatOp(f, inst, operation); 4025 4026 const lhs = try f.resolveInst(bin_op.lhs); 4027 const rhs = try f.resolveInst(bin_op.rhs); 4028 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 4029 4030 const writer = f.object.writer(); 4031 const local = try f.allocLocal(inst, inst_ty); 4032 const v = try Vectorize.start(f, inst, writer, inst_ty); 4033 try f.writeCValue(writer, local, .Other); 4034 try v.elem(f, writer); 4035 // (lhs <> rhs) ? lhs : rhs 4036 try writer.writeAll(" = ("); 4037 try f.writeCValue(writer, lhs, .Other); 4038 try v.elem(f, writer); 4039 try writer.writeByte(' '); 4040 try writer.writeByte(operator); 4041 try writer.writeByte(' '); 4042 try f.writeCValue(writer, rhs, .Other); 4043 try v.elem(f, writer); 4044 try writer.writeAll(") ? "); 4045 try f.writeCValue(writer, lhs, .Other); 4046 try v.elem(f, writer); 4047 try writer.writeAll(" : "); 4048 try f.writeCValue(writer, rhs, .Other); 4049 try v.elem(f, writer); 4050 try writer.writeAll(";\n"); 4051 try v.end(f, inst, writer); 4052 4053 return local; 4054 } 4055 4056 fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { 4057 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 4058 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; 4059 4060 const ptr = try f.resolveInst(bin_op.lhs); 4061 const len = try f.resolveInst(bin_op.rhs); 4062 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 4063 4064 const inst_ty = f.air.typeOfIndex(inst); 4065 var buf: Type.SlicePtrFieldTypeBuffer = undefined; 4066 const ptr_ty = inst_ty.slicePtrFieldType(&buf); 4067 4068 const writer = f.object.writer(); 4069 const local = try f.allocLocal(inst, inst_ty); 4070 { 4071 const a = try Assignment.start(f, writer, ptr_ty); 4072 try f.writeCValueMember(writer, local, .{ .identifier = "ptr" }); 4073 try a.assign(f, writer); 4074 try writer.writeByte('('); 4075 try f.renderType(writer, ptr_ty); 4076 try writer.writeByte(')'); 4077 try f.writeCValue(writer, ptr, .Other); 4078 try a.end(f, writer); 4079 } 4080 { 4081 const a = try Assignment.start(f, writer, Type.usize); 4082 try f.writeCValueMember(writer, local, .{ .identifier = "len" }); 4083 try a.assign(f, writer); 4084 try f.writeCValue(writer, len, .Other); 4085 try a.end(f, writer); 4086 } 4087 return local; 4088 } 4089 4090 fn airCall( 4091 f: *Function, 4092 inst: Air.Inst.Index, 4093 modifier: std.builtin.CallModifier, 4094 ) !CValue { 4095 // Not even allowed to call panic in a naked function. 4096 if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none; 4097 4098 const gpa = f.object.dg.gpa; 4099 const module = f.object.dg.module; 4100 const target = module.getTarget(); 4101 const writer = f.object.writer(); 4102 4103 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 4104 const extra = f.air.extraData(Air.Call, pl_op.payload); 4105 const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); 4106 4107 const resolved_args = try gpa.alloc(CValue, args.len); 4108 defer gpa.free(resolved_args); 4109 for (resolved_args, args) |*resolved_arg, arg| { 4110 const arg_ty = f.air.typeOf(arg); 4111 const arg_cty = try f.typeToIndex(arg_ty, .parameter); 4112 if (f.indexToCType(arg_cty).tag() == .void) { 4113 resolved_arg.* = .none; 4114 continue; 4115 } 4116 resolved_arg.* = try f.resolveInst(arg); 4117 if (arg_cty != try f.typeToIndex(arg_ty, .complete)) { 4118 var lowered_arg_buf: LowerFnRetTyBuffer = undefined; 4119 const lowered_arg_ty = lowerFnRetTy(arg_ty, &lowered_arg_buf, target); 4120 4121 const array_local = try f.allocLocal(inst, lowered_arg_ty); 4122 try writer.writeAll("memcpy("); 4123 try f.writeCValueMember(writer, array_local, .{ .identifier = "array" }); 4124 try writer.writeAll(", "); 4125 try f.writeCValue(writer, resolved_arg.*, .FunctionArgument); 4126 try writer.writeAll(", sizeof("); 4127 try f.renderType(writer, lowered_arg_ty); 4128 try writer.writeAll("));\n"); 4129 resolved_arg.* = array_local; 4130 } 4131 } 4132 4133 const callee = try f.resolveInst(pl_op.operand); 4134 4135 { 4136 var bt = iterateBigTomb(f, inst); 4137 try bt.feed(pl_op.operand); 4138 for (args) |arg| try bt.feed(arg); 4139 } 4140 4141 const callee_ty = f.air.typeOf(pl_op.operand); 4142 const fn_ty = switch (callee_ty.zigTypeTag()) { 4143 .Fn => callee_ty, 4144 .Pointer => callee_ty.childType(), 4145 else => unreachable, 4146 }; 4147 4148 const ret_ty = fn_ty.fnReturnType(); 4149 var lowered_ret_buf: LowerFnRetTyBuffer = undefined; 4150 const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); 4151 4152 const result_local = result: { 4153 if (modifier == .always_tail) { 4154 try writer.writeAll("zig_always_tail return "); 4155 break :result .none; 4156 } else if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { 4157 break :result .none; 4158 } else if (f.liveness.isUnused(inst)) { 4159 try writer.writeByte('('); 4160 try f.renderType(writer, Type.void); 4161 try writer.writeByte(')'); 4162 break :result .none; 4163 } else { 4164 const local = try f.allocLocal(inst, lowered_ret_ty); 4165 try f.writeCValue(writer, local, .Other); 4166 try writer.writeAll(" = "); 4167 break :result local; 4168 } 4169 }; 4170 4171 callee: { 4172 known: { 4173 const fn_decl = fn_decl: { 4174 const callee_val = f.air.value(pl_op.operand) orelse break :known; 4175 break :fn_decl switch (callee_val.tag()) { 4176 .extern_fn => callee_val.castTag(.extern_fn).?.data.owner_decl, 4177 .function => callee_val.castTag(.function).?.data.owner_decl, 4178 .decl_ref => callee_val.castTag(.decl_ref).?.data, 4179 else => break :known, 4180 }; 4181 }; 4182 switch (modifier) { 4183 .auto, .always_tail => try f.object.dg.renderDeclName(writer, fn_decl, 0), 4184 inline .never_tail, .never_inline => |mod| try writer.writeAll(try f.getLazyFnName( 4185 @unionInit(LazyFnKey, @tagName(mod), fn_decl), 4186 @unionInit(LazyFnValue.Data, @tagName(mod), {}), 4187 )), 4188 else => unreachable, 4189 } 4190 break :callee; 4191 } 4192 switch (modifier) { 4193 .auto, .always_tail => {}, 4194 .never_tail => return f.fail("CBE: runtime callee with never_tail attribute unsupported", .{}), 4195 .never_inline => return f.fail("CBE: runtime callee with never_inline attribute unsupported", .{}), 4196 else => unreachable, 4197 } 4198 // Fall back to function pointer call. 4199 try f.writeCValue(writer, callee, .Other); 4200 } 4201 4202 try writer.writeByte('('); 4203 var args_written: usize = 0; 4204 for (resolved_args) |resolved_arg| { 4205 if (resolved_arg == .none) continue; 4206 if (args_written != 0) try writer.writeAll(", "); 4207 try f.writeCValue(writer, resolved_arg, .FunctionArgument); 4208 if (resolved_arg == .new_local) try freeLocal(f, inst, resolved_arg.new_local, 0); 4209 args_written += 1; 4210 } 4211 try writer.writeAll(");\n"); 4212 4213 const result = result: { 4214 if (result_local == .none or !lowersToArray(ret_ty, target)) 4215 break :result result_local; 4216 4217 const array_local = try f.allocLocal(inst, ret_ty); 4218 try writer.writeAll("memcpy("); 4219 try f.writeCValue(writer, array_local, .FunctionArgument); 4220 try writer.writeAll(", "); 4221 try f.writeCValueMember(writer, result_local, .{ .identifier = "array" }); 4222 try writer.writeAll(", sizeof("); 4223 try f.renderType(writer, ret_ty); 4224 try writer.writeAll("));\n"); 4225 try freeLocal(f, inst, result_local.new_local, 0); 4226 break :result array_local; 4227 }; 4228 4229 return result; 4230 } 4231 4232 fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { 4233 const dbg_stmt = f.air.instructions.items(.data)[inst].dbg_stmt; 4234 const writer = f.object.writer(); 4235 // TODO re-evaluate whether to emit these or not. If we naively emit 4236 // these directives, the output file will report bogus line numbers because 4237 // every newline after the #line directive adds one to the line. 4238 // We also don't print the filename yet, so the output is strictly unhelpful. 4239 // If we wanted to go this route, we would need to go all the way and not output 4240 // newlines until the next dbg_stmt occurs. 4241 // Perhaps an additional compilation option is in order? 4242 //try writer.print("#line {d}\n", .{dbg_stmt.line + 1}); 4243 try writer.print("/* file:{d}:{d} */\n", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); 4244 return .none; 4245 } 4246 4247 fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { 4248 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 4249 const writer = f.object.writer(); 4250 const function = f.air.values[ty_pl.payload].castTag(.function).?.data; 4251 const mod = f.object.dg.module; 4252 try writer.print("/* dbg func:{s} */\n", .{mod.declPtr(function.owner_decl).name}); 4253 return .none; 4254 } 4255 4256 fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { 4257 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 4258 const name = f.air.nullTerminatedString(pl_op.payload); 4259 const operand_is_undef = if (f.air.value(pl_op.operand)) |v| v.isUndefDeep() else false; 4260 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand); 4261 4262 try reap(f, inst, &.{pl_op.operand}); 4263 const writer = f.object.writer(); 4264 try writer.print("/* var:{s} */\n", .{name}); 4265 return .none; 4266 } 4267 4268 fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { 4269 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 4270 const extra = f.air.extraData(Air.Block, ty_pl.payload); 4271 const body = f.air.extra[extra.end..][0..extra.data.body_len]; 4272 const liveness_block = f.liveness.getBlock(inst); 4273 4274 const block_id: usize = f.next_block_index; 4275 f.next_block_index += 1; 4276 const writer = f.object.writer(); 4277 4278 const inst_ty = f.air.typeOfIndex(inst); 4279 const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) 4280 try f.allocLocal(inst, inst_ty) 4281 else 4282 .none; 4283 4284 try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{ 4285 .block_id = block_id, 4286 .result = result, 4287 }); 4288 4289 try genBodyResolveState(f, inst, &.{}, body, true); 4290 4291 assert(f.blocks.remove(inst)); 4292 4293 // The body might result in some values we had beforehand being killed 4294 for (liveness_block.deaths) |death| { 4295 try die(f, inst, Air.indexToRef(death)); 4296 } 4297 4298 try f.object.indent_writer.insertNewline(); 4299 4300 // noreturn blocks have no `br` instructions reaching them, so we don't want a label 4301 if (!f.air.typeOfIndex(inst).isNoReturn()) { 4302 // label must be followed by an expression, include an empty one. 4303 try writer.print("zig_block_{d}:;\n", .{block_id}); 4304 } 4305 4306 return result; 4307 } 4308 4309 fn airTry(f: *Function, inst: Air.Inst.Index) !CValue { 4310 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 4311 const extra = f.air.extraData(Air.Try, pl_op.payload); 4312 const body = f.air.extra[extra.end..][0..extra.data.body_len]; 4313 const err_union_ty = f.air.typeOf(pl_op.operand); 4314 return lowerTry(f, inst, pl_op.operand, body, err_union_ty, false); 4315 } 4316 4317 fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue { 4318 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 4319 const extra = f.air.extraData(Air.TryPtr, ty_pl.payload); 4320 const body = f.air.extra[extra.end..][0..extra.data.body_len]; 4321 const err_union_ty = f.air.typeOf(extra.data.ptr).childType(); 4322 return lowerTry(f, inst, extra.data.ptr, body, err_union_ty, true); 4323 } 4324 4325 fn lowerTry( 4326 f: *Function, 4327 inst: Air.Inst.Index, 4328 operand: Air.Inst.Ref, 4329 body: []const Air.Inst.Index, 4330 err_union_ty: Type, 4331 is_ptr: bool, 4332 ) !CValue { 4333 const err_union = try f.resolveInst(operand); 4334 const inst_ty = f.air.typeOfIndex(inst); 4335 const liveness_condbr = f.liveness.getCondBr(inst); 4336 const writer = f.object.writer(); 4337 const payload_ty = err_union_ty.errorUnionPayload(); 4338 const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(); 4339 4340 if (!err_union_ty.errorUnionSet().errorSetIsEmpty()) { 4341 try writer.writeAll("if ("); 4342 if (!payload_has_bits) { 4343 if (is_ptr) 4344 try f.writeCValueDeref(writer, err_union) 4345 else 4346 try f.writeCValue(writer, err_union, .Other); 4347 } else { 4348 // Reap the operand so that it can be reused inside genBody. 4349 // Remember we must avoid calling reap() twice for the same operand 4350 // in this function. 4351 try reap(f, inst, &.{operand}); 4352 if (is_ptr) 4353 try f.writeCValueDerefMember(writer, err_union, .{ .identifier = "error" }) 4354 else 4355 try f.writeCValueMember(writer, err_union, .{ .identifier = "error" }); 4356 } 4357 try writer.writeAll(") "); 4358 4359 try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false); 4360 try f.object.indent_writer.insertNewline(); 4361 } 4362 4363 // Now we have the "then branch" (in terms of the liveness data); process any deaths. 4364 for (liveness_condbr.then_deaths) |death| { 4365 try die(f, inst, Air.indexToRef(death)); 4366 } 4367 4368 if (!payload_has_bits) { 4369 if (!is_ptr) { 4370 return .none; 4371 } else { 4372 return err_union; 4373 } 4374 } 4375 4376 try reap(f, inst, &.{operand}); 4377 4378 if (f.liveness.isUnused(inst)) { 4379 return .none; 4380 } 4381 4382 const local = try f.allocLocal(inst, inst_ty); 4383 const a = try Assignment.start(f, writer, inst_ty); 4384 try f.writeCValue(writer, local, .Other); 4385 try a.assign(f, writer); 4386 if (is_ptr) { 4387 try writer.writeByte('&'); 4388 try f.writeCValueDerefMember(writer, err_union, .{ .identifier = "payload" }); 4389 } else try f.writeCValueMember(writer, err_union, .{ .identifier = "payload" }); 4390 try a.end(f, writer); 4391 return local; 4392 } 4393 4394 fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { 4395 const branch = f.air.instructions.items(.data)[inst].br; 4396 const block = f.blocks.get(branch.block_inst).?; 4397 const result = block.result; 4398 const writer = f.object.writer(); 4399 4400 // If result is .none then the value of the block is unused. 4401 if (result != .none) { 4402 const operand_ty = f.air.typeOf(branch.operand); 4403 const operand = try f.resolveInst(branch.operand); 4404 try reap(f, inst, &.{branch.operand}); 4405 4406 const a = try Assignment.start(f, writer, operand_ty); 4407 try f.writeCValue(writer, result, .Other); 4408 try a.assign(f, writer); 4409 try f.writeCValue(writer, operand, .Other); 4410 try a.end(f, writer); 4411 } 4412 4413 try writer.print("goto zig_block_{d};\n", .{block.block_id}); 4414 return .none; 4415 } 4416 4417 fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { 4418 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 4419 const dest_ty = f.air.typeOfIndex(inst); 4420 4421 const operand = try f.resolveInst(ty_op.operand); 4422 const operand_ty = f.air.typeOf(ty_op.operand); 4423 4424 const bitcasted = try bitcast(f, dest_ty, operand, operand_ty); 4425 try reap(f, inst, &.{ty_op.operand}); 4426 return bitcasted.move(f, inst, dest_ty); 4427 } 4428 4429 const LocalResult = struct { 4430 c_value: CValue, 4431 need_free: bool, 4432 4433 fn move(lr: LocalResult, f: *Function, inst: Air.Inst.Index, dest_ty: Type) !CValue { 4434 if (lr.need_free) { 4435 // Move the freshly allocated local to be owned by this instruction, 4436 // by returning it here instead of freeing it. 4437 return lr.c_value; 4438 } 4439 4440 const local = try f.allocLocal(inst, dest_ty); 4441 try lr.free(f); 4442 const writer = f.object.writer(); 4443 try f.writeCValue(writer, local, .Other); 4444 if (dest_ty.isAbiInt()) { 4445 try writer.writeAll(" = "); 4446 } else { 4447 try writer.writeAll(" = ("); 4448 try f.renderType(writer, dest_ty); 4449 try writer.writeByte(')'); 4450 } 4451 try f.writeCValue(writer, lr.c_value, .Initializer); 4452 try writer.writeAll(";\n"); 4453 return local; 4454 } 4455 4456 fn free(lr: LocalResult, f: *Function) !void { 4457 if (lr.need_free) { 4458 try freeLocal(f, 0, lr.c_value.new_local, 0); 4459 } 4460 } 4461 }; 4462 4463 fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !LocalResult { 4464 const target = f.object.dg.module.getTarget(); 4465 const writer = f.object.writer(); 4466 4467 if (operand_ty.isAbiInt() and dest_ty.isAbiInt()) { 4468 const src_info = dest_ty.intInfo(target); 4469 const dest_info = operand_ty.intInfo(target); 4470 if (src_info.signedness == dest_info.signedness and 4471 src_info.bits == dest_info.bits) 4472 { 4473 return .{ 4474 .c_value = operand, 4475 .need_free = false, 4476 }; 4477 } 4478 } 4479 4480 if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) { 4481 const local = try f.allocLocal(0, dest_ty); 4482 try f.writeCValue(writer, local, .Other); 4483 try writer.writeAll(" = ("); 4484 try f.renderType(writer, dest_ty); 4485 try writer.writeByte(')'); 4486 try f.writeCValue(writer, operand, .Other); 4487 try writer.writeAll(";\n"); 4488 return .{ 4489 .c_value = local, 4490 .need_free = true, 4491 }; 4492 } 4493 4494 const operand_lval = if (operand == .constant) blk: { 4495 const operand_local = try f.allocLocal(0, operand_ty); 4496 try f.writeCValue(writer, operand_local, .Other); 4497 if (operand_ty.isAbiInt()) { 4498 try writer.writeAll(" = "); 4499 } else { 4500 try writer.writeAll(" = ("); 4501 try f.renderType(writer, operand_ty); 4502 try writer.writeByte(')'); 4503 } 4504 try f.writeCValue(writer, operand, .Initializer); 4505 try writer.writeAll(";\n"); 4506 break :blk operand_local; 4507 } else operand; 4508 4509 const local = try f.allocLocal(0, dest_ty); 4510 try writer.writeAll("memcpy(&"); 4511 try f.writeCValue(writer, local, .Other); 4512 try writer.writeAll(", &"); 4513 try f.writeCValue(writer, operand_lval, .Other); 4514 try writer.writeAll(", sizeof("); 4515 try f.renderType(writer, dest_ty); 4516 try writer.writeAll("));\n"); 4517 4518 // Ensure padding bits have the expected value. 4519 if (dest_ty.isAbiInt()) { 4520 const dest_cty = try f.typeToCType(dest_ty, .complete); 4521 const dest_info = dest_ty.intInfo(target); 4522 var info_ty_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dest_info.signedness) { 4523 .unsigned => .int_unsigned, 4524 .signed => .int_signed, 4525 } }, .data = dest_info.bits }; 4526 var wrap_cty: ?CType = null; 4527 var need_bitcasts = false; 4528 4529 try f.writeCValue(writer, local, .Other); 4530 if (dest_cty.castTag(.array)) |pl| { 4531 try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { 4532 .Little => pl.data.len - 1, 4533 .Big => 0, 4534 }}); 4535 const elem_cty = f.indexToCType(pl.data.elem_type); 4536 wrap_cty = elem_cty.toSignedness(dest_info.signedness); 4537 need_bitcasts = wrap_cty.?.tag() == .zig_i128; 4538 info_ty_pl.data -= 1; 4539 info_ty_pl.data %= @intCast(u16, f.byteSize(elem_cty) * 8); 4540 info_ty_pl.data += 1; 4541 } 4542 try writer.writeAll(" = "); 4543 if (need_bitcasts) { 4544 try writer.writeAll("zig_bitCast_"); 4545 try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?.toUnsigned()); 4546 try writer.writeByte('('); 4547 } 4548 try writer.writeAll("zig_wrap_"); 4549 const info_ty = Type.initPayload(&info_ty_pl.base); 4550 if (wrap_cty) |cty| 4551 try f.object.dg.renderCTypeForBuiltinFnName(writer, cty) 4552 else 4553 try f.object.dg.renderTypeForBuiltinFnName(writer, info_ty); 4554 try writer.writeByte('('); 4555 if (need_bitcasts) { 4556 try writer.writeAll("zig_bitCast_"); 4557 try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_cty.?); 4558 try writer.writeByte('('); 4559 } 4560 try f.writeCValue(writer, local, .Other); 4561 if (dest_cty.castTag(.array)) |pl| { 4562 try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) { 4563 .Little => pl.data.len - 1, 4564 .Big => 0, 4565 }}); 4566 } 4567 if (need_bitcasts) try writer.writeByte(')'); 4568 try f.object.dg.renderBuiltinInfo(writer, info_ty, .bits); 4569 if (need_bitcasts) try writer.writeByte(')'); 4570 try writer.writeAll(");\n"); 4571 } 4572 4573 if (operand == .constant) { 4574 try freeLocal(f, 0, operand_lval.new_local, 0); 4575 } 4576 4577 return .{ 4578 .c_value = local, 4579 .need_free = true, 4580 }; 4581 } 4582 4583 fn airTrap(writer: anytype) !CValue { 4584 try writer.writeAll("zig_trap();\n"); 4585 return .none; 4586 } 4587 4588 fn airBreakpoint(writer: anytype) !CValue { 4589 try writer.writeAll("zig_breakpoint();\n"); 4590 return .none; 4591 } 4592 4593 fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { 4594 const writer = f.object.writer(); 4595 const local = try f.allocLocal(inst, Type.usize); 4596 try f.writeCValue(writer, local, .Other); 4597 try writer.writeAll(" = ("); 4598 try f.renderType(writer, Type.usize); 4599 try writer.writeAll(")zig_return_address();\n"); 4600 return local; 4601 } 4602 4603 fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { 4604 const writer = f.object.writer(); 4605 const local = try f.allocLocal(inst, Type.usize); 4606 try f.writeCValue(writer, local, .Other); 4607 try writer.writeAll(" = ("); 4608 try f.renderType(writer, Type.usize); 4609 try writer.writeAll(")zig_frame_address();\n"); 4610 return local; 4611 } 4612 4613 fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { 4614 const atomic_order = f.air.instructions.items(.data)[inst].fence; 4615 const writer = f.object.writer(); 4616 4617 try writer.writeAll("zig_fence("); 4618 try writeMemoryOrder(writer, atomic_order); 4619 try writer.writeAll(");\n"); 4620 4621 return .none; 4622 } 4623 4624 fn airUnreach(f: *Function) !CValue { 4625 // Not even allowed to call unreachable in a naked function. 4626 if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none; 4627 4628 try f.object.writer().writeAll("zig_unreachable();\n"); 4629 return .none; 4630 } 4631 4632 fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { 4633 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 4634 const loop = f.air.extraData(Air.Block, ty_pl.payload); 4635 const body = f.air.extra[loop.end..][0..loop.data.body_len]; 4636 const writer = f.object.writer(); 4637 4638 try writer.writeAll("for (;;) "); 4639 try genBody(f, body); // no need to restore state, we're noreturn 4640 try writer.writeByte('\n'); 4641 4642 return .none; 4643 } 4644 4645 fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { 4646 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 4647 const cond = try f.resolveInst(pl_op.operand); 4648 try reap(f, inst, &.{pl_op.operand}); 4649 const extra = f.air.extraData(Air.CondBr, pl_op.payload); 4650 const then_body = f.air.extra[extra.end..][0..extra.data.then_body_len]; 4651 const else_body = f.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; 4652 const liveness_condbr = f.liveness.getCondBr(inst); 4653 const writer = f.object.writer(); 4654 4655 try writer.writeAll("if ("); 4656 try f.writeCValue(writer, cond, .Other); 4657 try writer.writeAll(") "); 4658 4659 try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false); 4660 4661 // We don't need to use `genBodyResolveState` for the else block, because this instruction is 4662 // noreturn so must terminate a body, therefore we don't need to leave `value_map` or 4663 // `free_locals_map` well defined (our parent is responsible for doing that). 4664 4665 for (liveness_condbr.else_deaths) |death| { 4666 try die(f, inst, Air.indexToRef(death)); 4667 } 4668 4669 // We never actually need an else block, because our branches are noreturn so must (for 4670 // instance) `br` to a block (label). 4671 4672 try genBodyInner(f, else_body); 4673 4674 return .none; 4675 } 4676 4677 fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { 4678 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 4679 const condition = try f.resolveInst(pl_op.operand); 4680 try reap(f, inst, &.{pl_op.operand}); 4681 const condition_ty = f.air.typeOf(pl_op.operand); 4682 const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload); 4683 const writer = f.object.writer(); 4684 4685 try writer.writeAll("switch ("); 4686 if (condition_ty.zigTypeTag() == .Bool) { 4687 try writer.writeByte('('); 4688 try f.renderType(writer, Type.u1); 4689 try writer.writeByte(')'); 4690 } else if (condition_ty.isPtrAtRuntime()) { 4691 try writer.writeByte('('); 4692 try f.renderType(writer, Type.usize); 4693 try writer.writeByte(')'); 4694 } 4695 try f.writeCValue(writer, condition, .Other); 4696 try writer.writeAll(") {"); 4697 f.object.indent_writer.pushIndent(); 4698 4699 const gpa = f.object.dg.gpa; 4700 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.data.cases_len + 1); 4701 defer gpa.free(liveness.deaths); 4702 4703 // On the final iteration we do not need to fix any state. This is because, like in the `else` 4704 // branch of a `cond_br`, our parent has to do it for this entire body anyway. 4705 const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0); 4706 4707 var extra_index: usize = switch_br.end; 4708 for (0..switch_br.data.cases_len) |case_i| { 4709 const case = f.air.extraData(Air.SwitchBr.Case, extra_index); 4710 const items = @ptrCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); 4711 const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len]; 4712 extra_index = case.end + case.data.items_len + case_body.len; 4713 4714 for (items) |item| { 4715 try f.object.indent_writer.insertNewline(); 4716 try writer.writeAll("case "); 4717 if (condition_ty.isPtrAtRuntime()) { 4718 try writer.writeByte('('); 4719 try f.renderType(writer, Type.usize); 4720 try writer.writeByte(')'); 4721 } 4722 try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?, .Other); 4723 try writer.writeByte(':'); 4724 } 4725 try writer.writeByte(' '); 4726 4727 if (case_i != last_case_i) { 4728 try genBodyResolveState(f, inst, liveness.deaths[case_i], case_body, false); 4729 } else { 4730 for (liveness.deaths[case_i]) |death| { 4731 try die(f, inst, Air.indexToRef(death)); 4732 } 4733 try genBody(f, case_body); 4734 } 4735 4736 // The case body must be noreturn so we don't need to insert a break. 4737 } 4738 4739 const else_body = f.air.extra[extra_index..][0..switch_br.data.else_body_len]; 4740 try f.object.indent_writer.insertNewline(); 4741 if (else_body.len > 0) { 4742 // Note that this must be the last case (i.e. the `last_case_i` case was not hit above) 4743 for (liveness.deaths[liveness.deaths.len - 1]) |death| { 4744 try die(f, inst, Air.indexToRef(death)); 4745 } 4746 try writer.writeAll("default: "); 4747 try genBody(f, else_body); 4748 } else { 4749 try writer.writeAll("default: zig_unreachable();"); 4750 } 4751 try f.object.indent_writer.insertNewline(); 4752 4753 f.object.indent_writer.popIndent(); 4754 try writer.writeAll("}\n"); 4755 return .none; 4756 } 4757 4758 fn asmInputNeedsLocal(constraint: []const u8, value: CValue) bool { 4759 return switch (constraint[0]) { 4760 '{' => true, 4761 'i', 'r' => false, 4762 else => value == .constant, 4763 }; 4764 } 4765 4766 fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { 4767 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 4768 const extra = f.air.extraData(Air.Asm, ty_pl.payload); 4769 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; 4770 const clobbers_len = @truncate(u31, extra.data.flags); 4771 const gpa = f.object.dg.gpa; 4772 var extra_i: usize = extra.end; 4773 const outputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); 4774 extra_i += outputs.len; 4775 const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); 4776 extra_i += inputs.len; 4777 4778 const result = result: { 4779 const writer = f.object.writer(); 4780 const inst_ty = f.air.typeOfIndex(inst); 4781 const local = if (inst_ty.hasRuntimeBitsIgnoreComptime()) local: { 4782 const local = try f.allocLocal(inst, inst_ty); 4783 if (f.wantSafety()) { 4784 try f.writeCValue(writer, local, .Other); 4785 try writer.writeAll(" = "); 4786 try f.writeCValue(writer, .{ .undef = inst_ty }, .Other); 4787 try writer.writeAll(";\n"); 4788 } 4789 break :local local; 4790 } else .none; 4791 4792 const locals_begin = @intCast(LocalIndex, f.locals.items.len); 4793 const constraints_extra_begin = extra_i; 4794 for (outputs) |output| { 4795 const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]); 4796 const constraint = mem.sliceTo(extra_bytes, 0); 4797 const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4798 // This equation accounts for the fact that even if we have exactly 4 bytes 4799 // for the string, we still use the next u32 for the null terminator. 4800 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4801 4802 if (constraint.len < 2 or constraint[0] != '=' or 4803 (constraint[1] == '{' and constraint[constraint.len - 1] != '}')) 4804 { 4805 return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); 4806 } 4807 4808 const is_reg = constraint[1] == '{'; 4809 if (is_reg) { 4810 const output_ty = if (output == .none) inst_ty else f.air.typeOf(output).childType(); 4811 try writer.writeAll("register "); 4812 const alignment = 0; 4813 const local_value = try f.allocLocalValue(output_ty, alignment); 4814 try f.allocs.put(gpa, local_value.new_local, false); 4815 try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete); 4816 try writer.writeAll(" __asm(\""); 4817 try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); 4818 try writer.writeAll("\")"); 4819 if (f.wantSafety()) { 4820 try writer.writeAll(" = "); 4821 try f.writeCValue(writer, .{ .undef = output_ty }, .Other); 4822 } 4823 try writer.writeAll(";\n"); 4824 } 4825 } 4826 for (inputs) |input| { 4827 const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]); 4828 const constraint = mem.sliceTo(extra_bytes, 0); 4829 const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4830 // This equation accounts for the fact that even if we have exactly 4 bytes 4831 // for the string, we still use the next u32 for the null terminator. 4832 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4833 4834 if (constraint.len < 1 or mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or 4835 (constraint[0] == '{' and constraint[constraint.len - 1] != '}')) 4836 { 4837 return f.fail("CBE: constraint not supported: '{s}'", .{constraint}); 4838 } 4839 4840 const is_reg = constraint[0] == '{'; 4841 const input_val = try f.resolveInst(input); 4842 if (asmInputNeedsLocal(constraint, input_val)) { 4843 const input_ty = f.air.typeOf(input); 4844 if (is_reg) try writer.writeAll("register "); 4845 const alignment = 0; 4846 const local_value = try f.allocLocalValue(input_ty, alignment); 4847 try f.allocs.put(gpa, local_value.new_local, false); 4848 try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete); 4849 if (is_reg) { 4850 try writer.writeAll(" __asm(\""); 4851 try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); 4852 try writer.writeAll("\")"); 4853 } 4854 try writer.writeAll(" = "); 4855 try f.writeCValue(writer, input_val, .Other); 4856 try writer.writeAll(";\n"); 4857 } 4858 } 4859 for (0..clobbers_len) |_| { 4860 const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra[extra_i..]), 0); 4861 // This equation accounts for the fact that even if we have exactly 4 bytes 4862 // for the string, we still use the next u32 for the null terminator. 4863 extra_i += clobber.len / 4 + 1; 4864 } 4865 4866 { 4867 const asm_source = mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; 4868 4869 var stack = std.heap.stackFallback(256, f.object.dg.gpa); 4870 const allocator = stack.get(); 4871 const fixed_asm_source = try allocator.alloc(u8, asm_source.len); 4872 defer allocator.free(fixed_asm_source); 4873 4874 var src_i: usize = 0; 4875 var dst_i: usize = 0; 4876 while (true) { 4877 const literal = mem.sliceTo(asm_source[src_i..], '%'); 4878 src_i += literal.len; 4879 4880 @memcpy(fixed_asm_source[dst_i..][0..literal.len], literal); 4881 dst_i += literal.len; 4882 4883 if (src_i >= asm_source.len) break; 4884 4885 src_i += 1; 4886 if (src_i >= asm_source.len) 4887 return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); 4888 4889 fixed_asm_source[dst_i] = '%'; 4890 dst_i += 1; 4891 4892 if (asm_source[src_i] != '[') { 4893 // This also handles %% 4894 fixed_asm_source[dst_i] = asm_source[src_i]; 4895 src_i += 1; 4896 dst_i += 1; 4897 continue; 4898 } 4899 4900 const desc = mem.sliceTo(asm_source[src_i..], ']'); 4901 if (mem.indexOfScalar(u8, desc, ':')) |colon| { 4902 const name = desc[0..colon]; 4903 const modifier = desc[colon + 1 ..]; 4904 4905 @memcpy(fixed_asm_source[dst_i..][0..modifier.len], modifier); 4906 dst_i += modifier.len; 4907 @memcpy(fixed_asm_source[dst_i..][0..name.len], name); 4908 dst_i += name.len; 4909 4910 src_i += desc.len; 4911 if (src_i >= asm_source.len) 4912 return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source}); 4913 } 4914 } 4915 4916 try writer.writeAll("__asm"); 4917 if (is_volatile) try writer.writeAll(" volatile"); 4918 try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)}); 4919 } 4920 4921 extra_i = constraints_extra_begin; 4922 var locals_index = locals_begin; 4923 try writer.writeByte(':'); 4924 for (outputs, 0..) |output, index| { 4925 const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]); 4926 const constraint = mem.sliceTo(extra_bytes, 0); 4927 const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4928 // This equation accounts for the fact that even if we have exactly 4 bytes 4929 // for the string, we still use the next u32 for the null terminator. 4930 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4931 4932 if (index > 0) try writer.writeByte(','); 4933 try writer.writeByte(' '); 4934 if (!mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); 4935 const is_reg = constraint[1] == '{'; 4936 try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)}); 4937 if (is_reg) { 4938 try f.writeCValue(writer, .{ .local = locals_index }, .Other); 4939 locals_index += 1; 4940 } else if (output == .none) { 4941 try f.writeCValue(writer, local, .FunctionArgument); 4942 } else { 4943 try f.writeCValueDeref(writer, try f.resolveInst(output)); 4944 } 4945 try writer.writeByte(')'); 4946 } 4947 try writer.writeByte(':'); 4948 for (inputs, 0..) |input, index| { 4949 const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]); 4950 const constraint = mem.sliceTo(extra_bytes, 0); 4951 const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4952 // This equation accounts for the fact that even if we have exactly 4 bytes 4953 // for the string, we still use the next u32 for the null terminator. 4954 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4955 4956 if (index > 0) try writer.writeByte(','); 4957 try writer.writeByte(' '); 4958 if (!mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); 4959 4960 const is_reg = constraint[0] == '{'; 4961 const input_val = try f.resolveInst(input); 4962 try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)}); 4963 try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { 4964 const input_local = .{ .local = locals_index }; 4965 locals_index += 1; 4966 break :local input_local; 4967 } else input_val, .Other); 4968 try writer.writeByte(')'); 4969 } 4970 try writer.writeByte(':'); 4971 for (0..clobbers_len) |clobber_i| { 4972 const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra[extra_i..]), 0); 4973 // This equation accounts for the fact that even if we have exactly 4 bytes 4974 // for the string, we still use the next u32 for the null terminator. 4975 extra_i += clobber.len / 4 + 1; 4976 4977 if (clobber.len == 0) continue; 4978 4979 if (clobber_i > 0) try writer.writeByte(','); 4980 try writer.print(" {s}", .{fmtStringLiteral(clobber, null)}); 4981 } 4982 try writer.writeAll(");\n"); 4983 4984 extra_i = constraints_extra_begin; 4985 locals_index = locals_begin; 4986 for (outputs) |output| { 4987 const extra_bytes = mem.sliceAsBytes(f.air.extra[extra_i..]); 4988 const constraint = mem.sliceTo(extra_bytes, 0); 4989 const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 4990 // This equation accounts for the fact that even if we have exactly 4 bytes 4991 // for the string, we still use the next u32 for the null terminator. 4992 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 4993 4994 const is_reg = constraint[1] == '{'; 4995 if (is_reg) { 4996 try f.writeCValueDeref(writer, if (output == .none) 4997 .{ .local_ref = local.new_local } 4998 else 4999 try f.resolveInst(output)); 5000 try writer.writeAll(" = "); 5001 try f.writeCValue(writer, .{ .local = locals_index }, .Other); 5002 locals_index += 1; 5003 try writer.writeAll(";\n"); 5004 } 5005 } 5006 5007 break :result if (f.liveness.isUnused(inst)) .none else local; 5008 }; 5009 5010 var bt = iterateBigTomb(f, inst); 5011 for (outputs) |output| { 5012 if (output == .none) continue; 5013 try bt.feed(output); 5014 } 5015 for (inputs) |input| { 5016 try bt.feed(input); 5017 } 5018 5019 return result; 5020 } 5021 5022 fn airIsNull( 5023 f: *Function, 5024 inst: Air.Inst.Index, 5025 operator: []const u8, 5026 is_ptr: bool, 5027 ) !CValue { 5028 const un_op = f.air.instructions.items(.data)[inst].un_op; 5029 5030 const writer = f.object.writer(); 5031 const operand = try f.resolveInst(un_op); 5032 try reap(f, inst, &.{un_op}); 5033 5034 const local = try f.allocLocal(inst, Type.bool); 5035 try f.writeCValue(writer, local, .Other); 5036 try writer.writeAll(" = "); 5037 if (is_ptr) { 5038 try f.writeCValueDeref(writer, operand); 5039 } else { 5040 try f.writeCValue(writer, operand, .Other); 5041 } 5042 5043 const operand_ty = f.air.typeOf(un_op); 5044 const optional_ty = if (is_ptr) operand_ty.childType() else operand_ty; 5045 var payload_buf: Type.Payload.ElemType = undefined; 5046 const payload_ty = optional_ty.optionalChild(&payload_buf); 5047 var slice_ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; 5048 5049 const rhs = if (!payload_ty.hasRuntimeBitsIgnoreComptime()) 5050 TypedValue{ .ty = Type.bool, .val = Value.true } 5051 else if (optional_ty.isPtrLikeOptional()) 5052 // operand is a regular pointer, test `operand !=/== NULL` 5053 TypedValue{ .ty = optional_ty, .val = Value.null } 5054 else if (payload_ty.zigTypeTag() == .ErrorSet) 5055 TypedValue{ .ty = payload_ty, .val = Value.zero } 5056 else if (payload_ty.isSlice() and optional_ty.optionalReprIsPayload()) rhs: { 5057 try writer.writeAll(".ptr"); 5058 const slice_ptr_ty = payload_ty.slicePtrFieldType(&slice_ptr_buf); 5059 break :rhs TypedValue{ .ty = slice_ptr_ty, .val = Value.null }; 5060 } else rhs: { 5061 try writer.writeAll(".is_null"); 5062 break :rhs TypedValue{ .ty = Type.bool, .val = Value.true }; 5063 }; 5064 try writer.writeByte(' '); 5065 try writer.writeAll(operator); 5066 try writer.writeByte(' '); 5067 try f.object.dg.renderValue(writer, rhs.ty, rhs.val, .Other); 5068 try writer.writeAll(";\n"); 5069 return local; 5070 } 5071 5072 fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { 5073 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5074 5075 const operand = try f.resolveInst(ty_op.operand); 5076 try reap(f, inst, &.{ty_op.operand}); 5077 const opt_ty = f.air.typeOf(ty_op.operand); 5078 5079 var buf: Type.Payload.ElemType = undefined; 5080 const payload_ty = opt_ty.optionalChild(&buf); 5081 5082 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 5083 return .none; 5084 } 5085 5086 const inst_ty = f.air.typeOfIndex(inst); 5087 const writer = f.object.writer(); 5088 const local = try f.allocLocal(inst, inst_ty); 5089 5090 if (opt_ty.optionalReprIsPayload()) { 5091 try f.writeCValue(writer, local, .Other); 5092 try writer.writeAll(" = "); 5093 try f.writeCValue(writer, operand, .Other); 5094 try writer.writeAll(";\n"); 5095 return local; 5096 } 5097 5098 const a = try Assignment.start(f, writer, inst_ty); 5099 try f.writeCValue(writer, local, .Other); 5100 try a.assign(f, writer); 5101 try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); 5102 try a.end(f, writer); 5103 return local; 5104 } 5105 5106 fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { 5107 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5108 5109 const writer = f.object.writer(); 5110 const operand = try f.resolveInst(ty_op.operand); 5111 try reap(f, inst, &.{ty_op.operand}); 5112 const ptr_ty = f.air.typeOf(ty_op.operand); 5113 const opt_ty = ptr_ty.childType(); 5114 const inst_ty = f.air.typeOfIndex(inst); 5115 5116 if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) { 5117 return .{ .undef = inst_ty }; 5118 } 5119 5120 const local = try f.allocLocal(inst, inst_ty); 5121 try f.writeCValue(writer, local, .Other); 5122 5123 if (opt_ty.optionalReprIsPayload()) { 5124 // the operand is just a regular pointer, no need to do anything special. 5125 // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C 5126 try writer.writeAll(" = "); 5127 try f.writeCValue(writer, operand, .Other); 5128 } else { 5129 try writer.writeAll(" = &"); 5130 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); 5131 } 5132 try writer.writeAll(";\n"); 5133 return local; 5134 } 5135 5136 fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { 5137 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5138 const writer = f.object.writer(); 5139 const operand = try f.resolveInst(ty_op.operand); 5140 try reap(f, inst, &.{ty_op.operand}); 5141 const operand_ty = f.air.typeOf(ty_op.operand); 5142 5143 const opt_ty = operand_ty.elemType(); 5144 5145 const inst_ty = f.air.typeOfIndex(inst); 5146 5147 if (opt_ty.optionalReprIsPayload()) { 5148 if (f.liveness.isUnused(inst)) { 5149 return .none; 5150 } 5151 const local = try f.allocLocal(inst, inst_ty); 5152 // The payload and the optional are the same value. 5153 // Setting to non-null will be done when the payload is set. 5154 try f.writeCValue(writer, local, .Other); 5155 try writer.writeAll(" = "); 5156 try f.writeCValue(writer, operand, .Other); 5157 try writer.writeAll(";\n"); 5158 return local; 5159 } else { 5160 try f.writeCValueDeref(writer, operand); 5161 try writer.writeAll(".is_null = "); 5162 try f.object.dg.renderValue(writer, Type.bool, Value.false, .Initializer); 5163 try writer.writeAll(";\n"); 5164 5165 if (f.liveness.isUnused(inst)) { 5166 return .none; 5167 } 5168 5169 const local = try f.allocLocal(inst, inst_ty); 5170 try f.writeCValue(writer, local, .Other); 5171 try writer.writeAll(" = &"); 5172 try f.writeCValueDeref(writer, operand); 5173 try writer.writeAll(".payload;\n"); 5174 return local; 5175 } 5176 } 5177 5178 fn fieldLocation( 5179 container_ty: Type, 5180 field_ptr_ty: Type, 5181 field_index: u32, 5182 target: std.Target, 5183 ) union(enum) { 5184 begin: void, 5185 field: CValue, 5186 byte_offset: u32, 5187 end: void, 5188 } { 5189 return switch (container_ty.zigTypeTag()) { 5190 .Struct => switch (container_ty.containerLayout()) { 5191 .Auto, .Extern => for (field_index..container_ty.structFieldCount()) |next_field_index| { 5192 if (container_ty.structFieldIsComptime(next_field_index)) continue; 5193 const field_ty = container_ty.structFieldType(next_field_index); 5194 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; 5195 5196 break .{ .field = if (container_ty.isSimpleTuple()) 5197 .{ .field = next_field_index } 5198 else 5199 .{ .identifier = container_ty.structFieldName(next_field_index) } }; 5200 } else if (container_ty.hasRuntimeBitsIgnoreComptime()) .end else .begin, 5201 .Packed => if (field_ptr_ty.ptrInfo().data.host_size == 0) 5202 .{ .byte_offset = container_ty.packedStructFieldByteOffset(field_index, target) } 5203 else 5204 .begin, 5205 }, 5206 .Union => switch (container_ty.containerLayout()) { 5207 .Auto, .Extern => { 5208 const field_ty = container_ty.structFieldType(field_index); 5209 if (!field_ty.hasRuntimeBitsIgnoreComptime()) 5210 return if (container_ty.unionTagTypeSafety() != null and 5211 !container_ty.unionHasAllZeroBitFieldTypes()) 5212 .{ .field = .{ .identifier = "payload" } } 5213 else 5214 .begin; 5215 const field_name = container_ty.unionFields().keys()[field_index]; 5216 return .{ .field = if (container_ty.unionTagTypeSafety()) |_| 5217 .{ .payload_identifier = field_name } 5218 else 5219 .{ .identifier = field_name } }; 5220 }, 5221 .Packed => .begin, 5222 }, 5223 .Pointer => switch (container_ty.ptrSize()) { 5224 .Slice => switch (field_index) { 5225 0 => .{ .field = .{ .identifier = "ptr" } }, 5226 1 => .{ .field = .{ .identifier = "len" } }, 5227 else => unreachable, 5228 }, 5229 .One, .Many, .C => unreachable, 5230 }, 5231 else => unreachable, 5232 }; 5233 } 5234 5235 fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { 5236 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 5237 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; 5238 5239 const container_ptr_val = try f.resolveInst(extra.struct_operand); 5240 try reap(f, inst, &.{extra.struct_operand}); 5241 const container_ptr_ty = f.air.typeOf(extra.struct_operand); 5242 return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, extra.field_index); 5243 } 5244 5245 fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue { 5246 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5247 5248 const container_ptr_val = try f.resolveInst(ty_op.operand); 5249 try reap(f, inst, &.{ty_op.operand}); 5250 const container_ptr_ty = f.air.typeOf(ty_op.operand); 5251 return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, index); 5252 } 5253 5254 fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { 5255 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 5256 const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; 5257 5258 const target = f.object.dg.module.getTarget(); 5259 const container_ptr_ty = f.air.typeOfIndex(inst); 5260 const container_ty = container_ptr_ty.childType(); 5261 5262 const field_ptr_ty = f.air.typeOf(extra.field_ptr); 5263 const field_ptr_val = try f.resolveInst(extra.field_ptr); 5264 try reap(f, inst, &.{extra.field_ptr}); 5265 5266 const writer = f.object.writer(); 5267 const local = try f.allocLocal(inst, container_ptr_ty); 5268 try f.writeCValue(writer, local, .Other); 5269 try writer.writeAll(" = ("); 5270 try f.renderType(writer, container_ptr_ty); 5271 try writer.writeByte(')'); 5272 5273 switch (fieldLocation(container_ty, field_ptr_ty, extra.field_index, target)) { 5274 .begin => try f.writeCValue(writer, field_ptr_val, .Initializer), 5275 .field => |field| { 5276 var u8_ptr_pl = field_ptr_ty.ptrInfo(); 5277 u8_ptr_pl.data.pointee_type = Type.u8; 5278 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); 5279 5280 try writer.writeAll("(("); 5281 try f.renderType(writer, u8_ptr_ty); 5282 try writer.writeByte(')'); 5283 try f.writeCValue(writer, field_ptr_val, .Other); 5284 try writer.writeAll(" - offsetof("); 5285 try f.renderType(writer, container_ty); 5286 try writer.writeAll(", "); 5287 try f.writeCValue(writer, field, .Other); 5288 try writer.writeAll("))"); 5289 }, 5290 .byte_offset => |byte_offset| { 5291 var u8_ptr_pl = field_ptr_ty.ptrInfo(); 5292 u8_ptr_pl.data.pointee_type = Type.u8; 5293 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); 5294 5295 var byte_offset_pl = Value.Payload.U64{ 5296 .base = .{ .tag = .int_u64 }, 5297 .data = byte_offset, 5298 }; 5299 const byte_offset_val = Value.initPayload(&byte_offset_pl.base); 5300 5301 try writer.writeAll("(("); 5302 try f.renderType(writer, u8_ptr_ty); 5303 try writer.writeByte(')'); 5304 try f.writeCValue(writer, field_ptr_val, .Other); 5305 try writer.print(" - {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); 5306 }, 5307 .end => { 5308 try f.writeCValue(writer, field_ptr_val, .Other); 5309 try writer.print(" - {}", .{try f.fmtIntLiteral(Type.usize, Value.one)}); 5310 }, 5311 } 5312 5313 try writer.writeAll(";\n"); 5314 return local; 5315 } 5316 5317 fn fieldPtr( 5318 f: *Function, 5319 inst: Air.Inst.Index, 5320 container_ptr_ty: Type, 5321 container_ptr_val: CValue, 5322 field_index: u32, 5323 ) !CValue { 5324 const target = f.object.dg.module.getTarget(); 5325 const container_ty = container_ptr_ty.elemType(); 5326 const field_ptr_ty = f.air.typeOfIndex(inst); 5327 5328 // Ensure complete type definition is visible before accessing fields. 5329 _ = try f.typeToIndex(container_ty, .complete); 5330 5331 const writer = f.object.writer(); 5332 const local = try f.allocLocal(inst, field_ptr_ty); 5333 try f.writeCValue(writer, local, .Other); 5334 try writer.writeAll(" = ("); 5335 try f.renderType(writer, field_ptr_ty); 5336 try writer.writeByte(')'); 5337 5338 switch (fieldLocation(container_ty, field_ptr_ty, field_index, target)) { 5339 .begin => try f.writeCValue(writer, container_ptr_val, .Initializer), 5340 .field => |field| { 5341 try writer.writeByte('&'); 5342 try f.writeCValueDerefMember(writer, container_ptr_val, field); 5343 }, 5344 .byte_offset => |byte_offset| { 5345 var u8_ptr_pl = field_ptr_ty.ptrInfo(); 5346 u8_ptr_pl.data.pointee_type = Type.u8; 5347 const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); 5348 5349 var byte_offset_pl = Value.Payload.U64{ 5350 .base = .{ .tag = .int_u64 }, 5351 .data = byte_offset, 5352 }; 5353 const byte_offset_val = Value.initPayload(&byte_offset_pl.base); 5354 5355 try writer.writeAll("(("); 5356 try f.renderType(writer, u8_ptr_ty); 5357 try writer.writeByte(')'); 5358 try f.writeCValue(writer, container_ptr_val, .Other); 5359 try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); 5360 }, 5361 .end => { 5362 try writer.writeByte('('); 5363 try f.writeCValue(writer, container_ptr_val, .Other); 5364 try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, Value.one)}); 5365 }, 5366 } 5367 5368 try writer.writeAll(";\n"); 5369 return local; 5370 } 5371 5372 fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { 5373 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 5374 const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; 5375 5376 const inst_ty = f.air.typeOfIndex(inst); 5377 if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { 5378 try reap(f, inst, &.{extra.struct_operand}); 5379 return .none; 5380 } 5381 5382 const target = f.object.dg.module.getTarget(); 5383 const struct_byval = try f.resolveInst(extra.struct_operand); 5384 try reap(f, inst, &.{extra.struct_operand}); 5385 const struct_ty = f.air.typeOf(extra.struct_operand); 5386 const writer = f.object.writer(); 5387 5388 // Ensure complete type definition is visible before accessing fields. 5389 _ = try f.typeToIndex(struct_ty, .complete); 5390 5391 const field_name: CValue = switch (struct_ty.tag()) { 5392 .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) { 5393 .Auto, .Extern => if (struct_ty.isSimpleTuple()) 5394 .{ .field = extra.field_index } 5395 else 5396 .{ .identifier = struct_ty.structFieldName(extra.field_index) }, 5397 .Packed => { 5398 const struct_obj = struct_ty.castTag(.@"struct").?.data; 5399 const int_info = struct_ty.intInfo(target); 5400 5401 var bit_offset_ty_pl = Type.Payload.Bits{ 5402 .base = .{ .tag = .int_unsigned }, 5403 .data = Type.smallestUnsignedBits(int_info.bits - 1), 5404 }; 5405 const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); 5406 5407 var bit_offset_val_pl: Value.Payload.U64 = .{ 5408 .base = .{ .tag = .int_u64 }, 5409 .data = struct_obj.packedFieldBitOffset(target, extra.field_index), 5410 }; 5411 const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); 5412 5413 const field_int_signedness = if (inst_ty.isAbiInt()) 5414 inst_ty.intInfo(target).signedness 5415 else 5416 .unsigned; 5417 var field_int_pl = Type.Payload.Bits{ 5418 .base = .{ .tag = switch (field_int_signedness) { 5419 .unsigned => .int_unsigned, 5420 .signed => .int_signed, 5421 } }, 5422 .data = @intCast(u16, inst_ty.bitSize(target)), 5423 }; 5424 const field_int_ty = Type.initPayload(&field_int_pl.base); 5425 5426 const temp_local = try f.allocLocal(inst, field_int_ty); 5427 try f.writeCValue(writer, temp_local, .Other); 5428 try writer.writeAll(" = zig_wrap_"); 5429 try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); 5430 try writer.writeAll("(("); 5431 try f.renderType(writer, field_int_ty); 5432 try writer.writeByte(')'); 5433 const cant_cast = int_info.bits > 64; 5434 if (cant_cast) { 5435 if (field_int_ty.bitSize(target) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{}); 5436 try writer.writeAll("zig_lo_"); 5437 try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty); 5438 try writer.writeByte('('); 5439 } 5440 if (bit_offset_val_pl.data > 0) { 5441 try writer.writeAll("zig_shr_"); 5442 try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty); 5443 try writer.writeByte('('); 5444 } 5445 try f.writeCValue(writer, struct_byval, .Other); 5446 if (bit_offset_val_pl.data > 0) { 5447 try writer.writeAll(", "); 5448 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); 5449 try writer.writeByte(')'); 5450 } 5451 if (cant_cast) try writer.writeByte(')'); 5452 try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits); 5453 try writer.writeAll(");\n"); 5454 if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local; 5455 5456 const local = try f.allocLocal(inst, inst_ty); 5457 try writer.writeAll("memcpy("); 5458 try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument); 5459 try writer.writeAll(", "); 5460 try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); 5461 try writer.writeAll(", sizeof("); 5462 try f.renderType(writer, inst_ty); 5463 try writer.writeAll("));\n"); 5464 try freeLocal(f, inst, temp_local.new_local, 0); 5465 return local; 5466 }, 5467 }, 5468 .@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout() == .Packed) { 5469 const operand_lval = if (struct_byval == .constant) blk: { 5470 const operand_local = try f.allocLocal(inst, struct_ty); 5471 try f.writeCValue(writer, operand_local, .Other); 5472 try writer.writeAll(" = "); 5473 try f.writeCValue(writer, struct_byval, .Initializer); 5474 try writer.writeAll(";\n"); 5475 break :blk operand_local; 5476 } else struct_byval; 5477 5478 const local = try f.allocLocal(inst, inst_ty); 5479 try writer.writeAll("memcpy(&"); 5480 try f.writeCValue(writer, local, .Other); 5481 try writer.writeAll(", &"); 5482 try f.writeCValue(writer, operand_lval, .Other); 5483 try writer.writeAll(", sizeof("); 5484 try f.renderType(writer, inst_ty); 5485 try writer.writeAll("));\n"); 5486 5487 if (struct_byval == .constant) { 5488 try freeLocal(f, inst, operand_lval.new_local, 0); 5489 } 5490 5491 return local; 5492 } else field_name: { 5493 const name = struct_ty.unionFields().keys()[extra.field_index]; 5494 break :field_name if (struct_ty.unionTagTypeSafety()) |_| 5495 .{ .payload_identifier = name } 5496 else 5497 .{ .identifier = name }; 5498 }, 5499 else => unreachable, 5500 }; 5501 5502 const local = try f.allocLocal(inst, inst_ty); 5503 const a = try Assignment.start(f, writer, inst_ty); 5504 try f.writeCValue(writer, local, .Other); 5505 try a.assign(f, writer); 5506 try f.writeCValueMember(writer, struct_byval, field_name); 5507 try a.end(f, writer); 5508 return local; 5509 } 5510 5511 /// *(E!T) -> E 5512 /// Note that the result is never a pointer. 5513 fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { 5514 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5515 5516 const inst_ty = f.air.typeOfIndex(inst); 5517 const operand = try f.resolveInst(ty_op.operand); 5518 const operand_ty = f.air.typeOf(ty_op.operand); 5519 try reap(f, inst, &.{ty_op.operand}); 5520 5521 const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer; 5522 const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; 5523 const error_ty = error_union_ty.errorUnionSet(); 5524 const payload_ty = error_union_ty.errorUnionPayload(); 5525 const local = try f.allocLocal(inst, inst_ty); 5526 5527 if (!payload_ty.hasRuntimeBits() and operand == .local and operand.local == local.new_local) { 5528 // The store will be 'x = x'; elide it. 5529 return local; 5530 } 5531 5532 const writer = f.object.writer(); 5533 try f.writeCValue(writer, local, .Other); 5534 try writer.writeAll(" = "); 5535 5536 if (!payload_ty.hasRuntimeBits()) { 5537 try f.writeCValue(writer, operand, .Other); 5538 } else { 5539 if (!error_ty.errorSetIsEmpty()) 5540 if (operand_is_ptr) 5541 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" }) 5542 else 5543 try f.writeCValueMember(writer, operand, .{ .identifier = "error" }) 5544 else 5545 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Initializer); 5546 } 5547 try writer.writeAll(";\n"); 5548 return local; 5549 } 5550 5551 fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { 5552 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5553 5554 const inst_ty = f.air.typeOfIndex(inst); 5555 const operand = try f.resolveInst(ty_op.operand); 5556 try reap(f, inst, &.{ty_op.operand}); 5557 const operand_ty = f.air.typeOf(ty_op.operand); 5558 const error_union_ty = if (is_ptr) operand_ty.childType() else operand_ty; 5559 5560 const writer = f.object.writer(); 5561 if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { 5562 if (!is_ptr) return .none; 5563 5564 const local = try f.allocLocal(inst, inst_ty); 5565 try f.writeCValue(writer, local, .Other); 5566 try writer.writeAll(" = ("); 5567 try f.renderType(writer, inst_ty); 5568 try writer.writeByte(')'); 5569 try f.writeCValue(writer, operand, .Initializer); 5570 try writer.writeAll(";\n"); 5571 return local; 5572 } 5573 5574 const local = try f.allocLocal(inst, inst_ty); 5575 const a = try Assignment.start(f, writer, inst_ty); 5576 try f.writeCValue(writer, local, .Other); 5577 try a.assign(f, writer); 5578 if (is_ptr) { 5579 try writer.writeByte('&'); 5580 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); 5581 } else try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); 5582 try a.end(f, writer); 5583 return local; 5584 } 5585 5586 fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { 5587 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5588 5589 const inst_ty = f.air.typeOfIndex(inst); 5590 const repr_is_payload = inst_ty.optionalReprIsPayload(); 5591 const payload_ty = f.air.typeOf(ty_op.operand); 5592 const payload = try f.resolveInst(ty_op.operand); 5593 try reap(f, inst, &.{ty_op.operand}); 5594 5595 const writer = f.object.writer(); 5596 const local = try f.allocLocal(inst, inst_ty); 5597 { 5598 const a = try Assignment.start(f, writer, payload_ty); 5599 if (repr_is_payload) 5600 try f.writeCValue(writer, local, .Other) 5601 else 5602 try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); 5603 try a.assign(f, writer); 5604 try f.writeCValue(writer, payload, .Other); 5605 try a.end(f, writer); 5606 } 5607 if (!repr_is_payload) { 5608 const a = try Assignment.start(f, writer, Type.bool); 5609 try f.writeCValueMember(writer, local, .{ .identifier = "is_null" }); 5610 try a.assign(f, writer); 5611 try f.object.dg.renderValue(writer, Type.bool, Value.false, .Other); 5612 try a.end(f, writer); 5613 } 5614 return local; 5615 } 5616 5617 fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { 5618 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5619 5620 const inst_ty = f.air.typeOfIndex(inst); 5621 const payload_ty = inst_ty.errorUnionPayload(); 5622 const repr_is_err = !payload_ty.hasRuntimeBitsIgnoreComptime(); 5623 const err_ty = inst_ty.errorUnionSet(); 5624 const err = try f.resolveInst(ty_op.operand); 5625 try reap(f, inst, &.{ty_op.operand}); 5626 5627 const writer = f.object.writer(); 5628 const local = try f.allocLocal(inst, inst_ty); 5629 5630 if (repr_is_err and err == .local and err.local == local.new_local) { 5631 // The store will be 'x = x'; elide it. 5632 return local; 5633 } 5634 5635 if (!repr_is_err) { 5636 const a = try Assignment.start(f, writer, payload_ty); 5637 try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); 5638 try a.assign(f, writer); 5639 try f.object.dg.renderValue(writer, payload_ty, Value.undef, .Other); 5640 try a.end(f, writer); 5641 } 5642 { 5643 const a = try Assignment.start(f, writer, err_ty); 5644 if (repr_is_err) 5645 try f.writeCValue(writer, local, .Other) 5646 else 5647 try f.writeCValueMember(writer, local, .{ .identifier = "error" }); 5648 try a.assign(f, writer); 5649 try f.writeCValue(writer, err, .Other); 5650 try a.end(f, writer); 5651 } 5652 return local; 5653 } 5654 5655 fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { 5656 const writer = f.object.writer(); 5657 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5658 const operand = try f.resolveInst(ty_op.operand); 5659 const error_union_ty = f.air.typeOf(ty_op.operand).childType(); 5660 5661 const error_ty = error_union_ty.errorUnionSet(); 5662 const payload_ty = error_union_ty.errorUnionPayload(); 5663 5664 // First, set the non-error value. 5665 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 5666 try f.writeCValueDeref(writer, operand); 5667 try writer.writeAll(" = "); 5668 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); 5669 try writer.writeAll(";\n "); 5670 5671 return operand; 5672 } 5673 try reap(f, inst, &.{ty_op.operand}); 5674 try f.writeCValueDeref(writer, operand); 5675 try writer.writeAll(".error = "); 5676 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); 5677 try writer.writeAll(";\n"); 5678 5679 // Then return the payload pointer (only if it is used) 5680 if (f.liveness.isUnused(inst)) return .none; 5681 5682 const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); 5683 try f.writeCValue(writer, local, .Other); 5684 try writer.writeAll(" = &("); 5685 try f.writeCValueDeref(writer, operand); 5686 try writer.writeAll(").payload;\n"); 5687 return local; 5688 } 5689 5690 fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { 5691 _ = inst; 5692 return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); 5693 } 5694 5695 fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { 5696 _ = inst; 5697 return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{}); 5698 } 5699 5700 fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue { 5701 _ = inst; 5702 return f.fail("TODO: C backend: implement airSaveErrReturnTraceIndex", .{}); 5703 } 5704 5705 fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { 5706 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5707 5708 const inst_ty = f.air.typeOfIndex(inst); 5709 const payload_ty = inst_ty.errorUnionPayload(); 5710 const payload = try f.resolveInst(ty_op.operand); 5711 const repr_is_err = !payload_ty.hasRuntimeBitsIgnoreComptime(); 5712 const err_ty = inst_ty.errorUnionSet(); 5713 try reap(f, inst, &.{ty_op.operand}); 5714 5715 const writer = f.object.writer(); 5716 const local = try f.allocLocal(inst, inst_ty); 5717 if (!repr_is_err) { 5718 const a = try Assignment.start(f, writer, payload_ty); 5719 try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); 5720 try a.assign(f, writer); 5721 try f.writeCValue(writer, payload, .Other); 5722 try a.end(f, writer); 5723 } 5724 { 5725 const a = try Assignment.start(f, writer, err_ty); 5726 if (repr_is_err) 5727 try f.writeCValue(writer, local, .Other) 5728 else 5729 try f.writeCValueMember(writer, local, .{ .identifier = "error" }); 5730 try a.assign(f, writer); 5731 try f.object.dg.renderValue(writer, err_ty, Value.zero, .Other); 5732 try a.end(f, writer); 5733 } 5734 return local; 5735 } 5736 5737 fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue { 5738 const un_op = f.air.instructions.items(.data)[inst].un_op; 5739 5740 const writer = f.object.writer(); 5741 const operand = try f.resolveInst(un_op); 5742 try reap(f, inst, &.{un_op}); 5743 const operand_ty = f.air.typeOf(un_op); 5744 const local = try f.allocLocal(inst, Type.bool); 5745 const err_union_ty = if (is_ptr) operand_ty.childType() else operand_ty; 5746 const payload_ty = err_union_ty.errorUnionPayload(); 5747 const error_ty = err_union_ty.errorUnionSet(); 5748 5749 try f.writeCValue(writer, local, .Other); 5750 try writer.writeAll(" = "); 5751 5752 if (!error_ty.errorSetIsEmpty()) 5753 if (payload_ty.hasRuntimeBits()) 5754 if (is_ptr) 5755 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" }) 5756 else 5757 try f.writeCValueMember(writer, operand, .{ .identifier = "error" }) 5758 else 5759 try f.writeCValue(writer, operand, .Other) 5760 else 5761 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); 5762 try writer.writeByte(' '); 5763 try writer.writeAll(operator); 5764 try writer.writeByte(' '); 5765 try f.object.dg.renderValue(writer, error_ty, Value.zero, .Other); 5766 try writer.writeAll(";\n"); 5767 return local; 5768 } 5769 5770 fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { 5771 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5772 5773 const operand = try f.resolveInst(ty_op.operand); 5774 try reap(f, inst, &.{ty_op.operand}); 5775 const inst_ty = f.air.typeOfIndex(inst); 5776 const writer = f.object.writer(); 5777 const local = try f.allocLocal(inst, inst_ty); 5778 const array_ty = f.air.typeOf(ty_op.operand).childType(); 5779 5780 try f.writeCValueMember(writer, local, .{ .identifier = "ptr" }); 5781 try writer.writeAll(" = "); 5782 // Unfortunately, C does not support any equivalent to 5783 // &(*(void *)p)[0], although LLVM does via GetElementPtr 5784 if (operand == .undef) { 5785 var buf: Type.SlicePtrFieldTypeBuffer = undefined; 5786 try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); 5787 } else if (array_ty.hasRuntimeBitsIgnoreComptime()) { 5788 try writer.writeAll("&("); 5789 try f.writeCValueDeref(writer, operand); 5790 try writer.print(")[{}]", .{try f.fmtIntLiteral(Type.usize, Value.zero)}); 5791 } else try f.writeCValue(writer, operand, .Initializer); 5792 try writer.writeAll("; "); 5793 5794 const array_len = array_ty.arrayLen(); 5795 var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len }; 5796 const len_val = Value.initPayload(&len_pl.base); 5797 try f.writeCValueMember(writer, local, .{ .identifier = "len" }); 5798 try writer.print(" = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)}); 5799 5800 return local; 5801 } 5802 5803 fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { 5804 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5805 5806 const inst_ty = f.air.typeOfIndex(inst); 5807 const operand = try f.resolveInst(ty_op.operand); 5808 try reap(f, inst, &.{ty_op.operand}); 5809 const operand_ty = f.air.typeOf(ty_op.operand); 5810 const target = f.object.dg.module.getTarget(); 5811 const operation = if (inst_ty.isRuntimeFloat() and operand_ty.isRuntimeFloat()) 5812 if (inst_ty.floatBits(target) < operand_ty.floatBits(target)) "trunc" else "extend" 5813 else if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) 5814 if (inst_ty.isSignedInt()) "fix" else "fixuns" 5815 else if (inst_ty.isRuntimeFloat() and operand_ty.isInt()) 5816 if (operand_ty.isSignedInt()) "float" else "floatun" 5817 else 5818 unreachable; 5819 5820 const writer = f.object.writer(); 5821 const local = try f.allocLocal(inst, inst_ty); 5822 try f.writeCValue(writer, local, .Other); 5823 5824 try writer.writeAll(" = "); 5825 if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) { 5826 try writer.writeAll("zig_wrap_"); 5827 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); 5828 try writer.writeByte('('); 5829 } 5830 try writer.writeAll("zig_"); 5831 try writer.writeAll(operation); 5832 try writer.writeAll(compilerRtAbbrev(operand_ty, target)); 5833 try writer.writeAll(compilerRtAbbrev(inst_ty, target)); 5834 try writer.writeByte('('); 5835 try f.writeCValue(writer, operand, .FunctionArgument); 5836 try writer.writeByte(')'); 5837 if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) { 5838 try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); 5839 try writer.writeByte(')'); 5840 } 5841 try writer.writeAll(";\n"); 5842 return local; 5843 } 5844 5845 fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { 5846 const un_op = f.air.instructions.items(.data)[inst].un_op; 5847 5848 const operand = try f.resolveInst(un_op); 5849 const operand_ty = f.air.typeOf(un_op); 5850 try reap(f, inst, &.{un_op}); 5851 const inst_ty = f.air.typeOfIndex(inst); 5852 const writer = f.object.writer(); 5853 const local = try f.allocLocal(inst, inst_ty); 5854 try f.writeCValue(writer, local, .Other); 5855 5856 try writer.writeAll(" = ("); 5857 try f.renderType(writer, inst_ty); 5858 try writer.writeByte(')'); 5859 if (operand_ty.isSlice()) { 5860 try f.writeCValueMember(writer, operand, .{ .identifier = "len" }); 5861 } else { 5862 try f.writeCValue(writer, operand, .Other); 5863 } 5864 try writer.writeAll(";\n"); 5865 return local; 5866 } 5867 5868 fn airUnBuiltinCall( 5869 f: *Function, 5870 inst: Air.Inst.Index, 5871 operation: []const u8, 5872 info: BuiltinInfo, 5873 ) !CValue { 5874 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 5875 5876 const operand = try f.resolveInst(ty_op.operand); 5877 try reap(f, inst, &.{ty_op.operand}); 5878 const inst_ty = f.air.typeOfIndex(inst); 5879 const inst_scalar_ty = inst_ty.scalarType(); 5880 const operand_ty = f.air.typeOf(ty_op.operand); 5881 const scalar_ty = operand_ty.scalarType(); 5882 5883 const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); 5884 const ref_ret = inst_scalar_cty.tag() == .array; 5885 5886 const writer = f.object.writer(); 5887 const local = try f.allocLocal(inst, inst_ty); 5888 const v = try Vectorize.start(f, inst, writer, operand_ty); 5889 if (!ref_ret) { 5890 try f.writeCValue(writer, local, .Other); 5891 try v.elem(f, writer); 5892 try writer.writeAll(" = "); 5893 } 5894 try writer.print("zig_{s}_", .{operation}); 5895 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 5896 try writer.writeByte('('); 5897 if (ref_ret) { 5898 try f.writeCValue(writer, local, .FunctionArgument); 5899 try v.elem(f, writer); 5900 try writer.writeAll(", "); 5901 } 5902 try f.writeCValue(writer, operand, .FunctionArgument); 5903 try v.elem(f, writer); 5904 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); 5905 try writer.writeAll(");\n"); 5906 try v.end(f, inst, writer); 5907 5908 return local; 5909 } 5910 5911 fn airBinBuiltinCall( 5912 f: *Function, 5913 inst: Air.Inst.Index, 5914 operation: []const u8, 5915 info: BuiltinInfo, 5916 ) !CValue { 5917 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 5918 5919 const operand_ty = f.air.typeOf(bin_op.lhs); 5920 const operand_cty = try f.typeToCType(operand_ty, .complete); 5921 const is_big = operand_cty.tag() == .array; 5922 5923 const lhs = try f.resolveInst(bin_op.lhs); 5924 const rhs = try f.resolveInst(bin_op.rhs); 5925 if (!is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 5926 5927 const inst_ty = f.air.typeOfIndex(inst); 5928 const inst_scalar_ty = inst_ty.scalarType(); 5929 const scalar_ty = operand_ty.scalarType(); 5930 5931 const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); 5932 const ref_ret = inst_scalar_cty.tag() == .array; 5933 5934 const writer = f.object.writer(); 5935 const local = try f.allocLocal(inst, inst_ty); 5936 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 5937 const v = try Vectorize.start(f, inst, writer, operand_ty); 5938 if (!ref_ret) { 5939 try f.writeCValue(writer, local, .Other); 5940 try v.elem(f, writer); 5941 try writer.writeAll(" = "); 5942 } 5943 try writer.print("zig_{s}_", .{operation}); 5944 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 5945 try writer.writeByte('('); 5946 if (ref_ret) { 5947 try f.writeCValue(writer, local, .FunctionArgument); 5948 try v.elem(f, writer); 5949 try writer.writeAll(", "); 5950 } 5951 try f.writeCValue(writer, lhs, .FunctionArgument); 5952 try v.elem(f, writer); 5953 try writer.writeAll(", "); 5954 try f.writeCValue(writer, rhs, .FunctionArgument); 5955 try v.elem(f, writer); 5956 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); 5957 try writer.writeAll(");\n"); 5958 try v.end(f, inst, writer); 5959 5960 return local; 5961 } 5962 5963 fn airCmpBuiltinCall( 5964 f: *Function, 5965 inst: Air.Inst.Index, 5966 data: anytype, 5967 operator: std.math.CompareOperator, 5968 operation: enum { cmp, operator }, 5969 info: BuiltinInfo, 5970 ) !CValue { 5971 const lhs = try f.resolveInst(data.lhs); 5972 const rhs = try f.resolveInst(data.rhs); 5973 try reap(f, inst, &.{ data.lhs, data.rhs }); 5974 5975 const inst_ty = f.air.typeOfIndex(inst); 5976 const inst_scalar_ty = inst_ty.scalarType(); 5977 const operand_ty = f.air.typeOf(data.lhs); 5978 const scalar_ty = operand_ty.scalarType(); 5979 5980 const inst_scalar_cty = try f.typeToCType(inst_scalar_ty, .complete); 5981 const ref_ret = inst_scalar_cty.tag() == .array; 5982 5983 const writer = f.object.writer(); 5984 const local = try f.allocLocal(inst, inst_ty); 5985 const v = try Vectorize.start(f, inst, writer, operand_ty); 5986 if (!ref_ret) { 5987 try f.writeCValue(writer, local, .Other); 5988 try v.elem(f, writer); 5989 try writer.writeAll(" = "); 5990 } 5991 try writer.print("zig_{s}_", .{switch (operation) { 5992 else => @tagName(operation), 5993 .operator => compareOperatorAbbrev(operator), 5994 }}); 5995 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 5996 try writer.writeByte('('); 5997 if (ref_ret) { 5998 try f.writeCValue(writer, local, .FunctionArgument); 5999 try v.elem(f, writer); 6000 try writer.writeAll(", "); 6001 } 6002 try f.writeCValue(writer, lhs, .FunctionArgument); 6003 try v.elem(f, writer); 6004 try writer.writeAll(", "); 6005 try f.writeCValue(writer, rhs, .FunctionArgument); 6006 try v.elem(f, writer); 6007 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); 6008 try writer.writeByte(')'); 6009 if (!ref_ret) try writer.print(" {s} {}", .{ 6010 compareOperatorC(operator), 6011 try f.fmtIntLiteral(Type.initTag(.i32), Value.zero), 6012 }); 6013 try writer.writeAll(";\n"); 6014 try v.end(f, inst, writer); 6015 6016 return local; 6017 } 6018 6019 fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { 6020 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 6021 const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data; 6022 const inst_ty = f.air.typeOfIndex(inst); 6023 const ptr = try f.resolveInst(extra.ptr); 6024 const expected_value = try f.resolveInst(extra.expected_value); 6025 const new_value = try f.resolveInst(extra.new_value); 6026 const ptr_ty = f.air.typeOf(extra.ptr); 6027 const ty = ptr_ty.childType(); 6028 6029 const writer = f.object.writer(); 6030 const new_value_mat = try Materialize.start(f, inst, writer, ty, new_value); 6031 try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value }); 6032 6033 const target = f.object.dg.module.getTarget(); 6034 var repr_pl = Type.Payload.Bits{ 6035 .base = .{ .tag = .int_unsigned }, 6036 .data = @intCast(u16, ty.abiSize(target) * 8), 6037 }; 6038 const repr_ty = if (ty.isRuntimeFloat()) Type.initPayload(&repr_pl.base) else ty; 6039 6040 const local = try f.allocLocal(inst, inst_ty); 6041 if (inst_ty.isPtrLikeOptional()) { 6042 { 6043 const a = try Assignment.start(f, writer, ty); 6044 try f.writeCValue(writer, local, .Other); 6045 try a.assign(f, writer); 6046 try f.writeCValue(writer, expected_value, .Other); 6047 try a.end(f, writer); 6048 } 6049 6050 try writer.writeAll("if ("); 6051 try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); 6052 try f.renderType(writer, ty); 6053 try writer.writeByte(')'); 6054 if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); 6055 try writer.writeAll(" *)"); 6056 try f.writeCValue(writer, ptr, .Other); 6057 try writer.writeAll(", "); 6058 try f.writeCValue(writer, local, .FunctionArgument); 6059 try writer.writeAll(", "); 6060 try new_value_mat.mat(f, writer); 6061 try writer.writeAll(", "); 6062 try writeMemoryOrder(writer, extra.successOrder()); 6063 try writer.writeAll(", "); 6064 try writeMemoryOrder(writer, extra.failureOrder()); 6065 try writer.writeAll(", "); 6066 try f.object.dg.renderTypeForBuiltinFnName(writer, ty); 6067 try writer.writeAll(", "); 6068 try f.object.dg.renderType(writer, repr_ty); 6069 try writer.writeByte(')'); 6070 try writer.writeAll(") {\n"); 6071 f.object.indent_writer.pushIndent(); 6072 { 6073 const a = try Assignment.start(f, writer, ty); 6074 try f.writeCValue(writer, local, .Other); 6075 try a.assign(f, writer); 6076 try writer.writeAll("NULL"); 6077 try a.end(f, writer); 6078 } 6079 f.object.indent_writer.popIndent(); 6080 try writer.writeAll("}\n"); 6081 } else { 6082 { 6083 const a = try Assignment.start(f, writer, ty); 6084 try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); 6085 try a.assign(f, writer); 6086 try f.writeCValue(writer, expected_value, .Other); 6087 try a.end(f, writer); 6088 } 6089 { 6090 const a = try Assignment.start(f, writer, Type.bool); 6091 try f.writeCValueMember(writer, local, .{ .identifier = "is_null" }); 6092 try a.assign(f, writer); 6093 try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); 6094 try f.renderType(writer, ty); 6095 try writer.writeByte(')'); 6096 if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); 6097 try writer.writeAll(" *)"); 6098 try f.writeCValue(writer, ptr, .Other); 6099 try writer.writeAll(", "); 6100 try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); 6101 try writer.writeAll(", "); 6102 try new_value_mat.mat(f, writer); 6103 try writer.writeAll(", "); 6104 try writeMemoryOrder(writer, extra.successOrder()); 6105 try writer.writeAll(", "); 6106 try writeMemoryOrder(writer, extra.failureOrder()); 6107 try writer.writeAll(", "); 6108 try f.object.dg.renderTypeForBuiltinFnName(writer, ty); 6109 try writer.writeAll(", "); 6110 try f.object.dg.renderType(writer, repr_ty); 6111 try writer.writeByte(')'); 6112 try a.end(f, writer); 6113 } 6114 } 6115 try new_value_mat.end(f, inst); 6116 6117 if (f.liveness.isUnused(inst)) { 6118 try freeLocal(f, inst, local.new_local, 0); 6119 return .none; 6120 } 6121 6122 return local; 6123 } 6124 6125 fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { 6126 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 6127 const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data; 6128 const inst_ty = f.air.typeOfIndex(inst); 6129 const ptr_ty = f.air.typeOf(pl_op.operand); 6130 const ty = ptr_ty.childType(); 6131 const ptr = try f.resolveInst(pl_op.operand); 6132 const operand = try f.resolveInst(extra.operand); 6133 6134 const writer = f.object.writer(); 6135 const operand_mat = try Materialize.start(f, inst, writer, ty, operand); 6136 try reap(f, inst, &.{ pl_op.operand, extra.operand }); 6137 6138 const target = f.object.dg.module.getTarget(); 6139 var repr_pl = Type.Payload.Bits{ 6140 .base = .{ .tag = .int_unsigned }, 6141 .data = @intCast(u16, ty.abiSize(target) * 8), 6142 }; 6143 const is_float = ty.isRuntimeFloat(); 6144 const is_128 = repr_pl.data == 128; 6145 const repr_ty = if (is_float) Type.initPayload(&repr_pl.base) else ty; 6146 6147 const local = try f.allocLocal(inst, inst_ty); 6148 try writer.print("zig_atomicrmw_{s}", .{toAtomicRmwSuffix(extra.op())}); 6149 if (is_float) try writer.writeAll("_float") else if (is_128) try writer.writeAll("_int128"); 6150 try writer.writeByte('('); 6151 try f.writeCValue(writer, local, .Other); 6152 try writer.writeAll(", ("); 6153 const use_atomic = switch (extra.op()) { 6154 else => true, 6155 // These are missing from stdatomic.h, so no atomic types unless a fallback is used. 6156 .Nand, .Min, .Max => is_float or is_128, 6157 }; 6158 if (use_atomic) try writer.writeAll("zig_atomic("); 6159 try f.renderType(writer, ty); 6160 if (use_atomic) try writer.writeByte(')'); 6161 if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); 6162 try writer.writeAll(" *)"); 6163 try f.writeCValue(writer, ptr, .Other); 6164 try writer.writeAll(", "); 6165 try operand_mat.mat(f, writer); 6166 try writer.writeAll(", "); 6167 try writeMemoryOrder(writer, extra.ordering()); 6168 try writer.writeAll(", "); 6169 try f.object.dg.renderTypeForBuiltinFnName(writer, ty); 6170 try writer.writeAll(", "); 6171 try f.object.dg.renderType(writer, repr_ty); 6172 try writer.writeAll(");\n"); 6173 try operand_mat.end(f, inst); 6174 6175 if (f.liveness.isUnused(inst)) { 6176 try freeLocal(f, inst, local.new_local, 0); 6177 return .none; 6178 } 6179 6180 return local; 6181 } 6182 6183 fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { 6184 const atomic_load = f.air.instructions.items(.data)[inst].atomic_load; 6185 const ptr = try f.resolveInst(atomic_load.ptr); 6186 try reap(f, inst, &.{atomic_load.ptr}); 6187 const ptr_ty = f.air.typeOf(atomic_load.ptr); 6188 const ty = ptr_ty.childType(); 6189 6190 const target = f.object.dg.module.getTarget(); 6191 var repr_pl = Type.Payload.Bits{ 6192 .base = .{ .tag = .int_unsigned }, 6193 .data = @intCast(u16, ty.abiSize(target) * 8), 6194 }; 6195 const repr_ty = if (ty.isRuntimeFloat()) Type.initPayload(&repr_pl.base) else ty; 6196 6197 const inst_ty = f.air.typeOfIndex(inst); 6198 const writer = f.object.writer(); 6199 const local = try f.allocLocal(inst, inst_ty); 6200 6201 try writer.writeAll("zig_atomic_load("); 6202 try f.writeCValue(writer, local, .Other); 6203 try writer.writeAll(", (zig_atomic("); 6204 try f.renderType(writer, ty); 6205 try writer.writeByte(')'); 6206 if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); 6207 try writer.writeAll(" *)"); 6208 try f.writeCValue(writer, ptr, .Other); 6209 try writer.writeAll(", "); 6210 try writeMemoryOrder(writer, atomic_load.order); 6211 try writer.writeAll(", "); 6212 try f.object.dg.renderTypeForBuiltinFnName(writer, ty); 6213 try writer.writeAll(", "); 6214 try f.object.dg.renderType(writer, repr_ty); 6215 try writer.writeAll(");\n"); 6216 6217 return local; 6218 } 6219 6220 fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CValue { 6221 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 6222 const ptr_ty = f.air.typeOf(bin_op.lhs); 6223 const ty = ptr_ty.childType(); 6224 const ptr = try f.resolveInst(bin_op.lhs); 6225 const element = try f.resolveInst(bin_op.rhs); 6226 6227 const writer = f.object.writer(); 6228 const element_mat = try Materialize.start(f, inst, writer, ty, element); 6229 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 6230 6231 const target = f.object.dg.module.getTarget(); 6232 var repr_pl = Type.Payload.Bits{ 6233 .base = .{ .tag = .int_unsigned }, 6234 .data = @intCast(u16, ty.abiSize(target) * 8), 6235 }; 6236 const repr_ty = if (ty.isRuntimeFloat()) Type.initPayload(&repr_pl.base) else ty; 6237 6238 try writer.writeAll("zig_atomic_store((zig_atomic("); 6239 try f.renderType(writer, ty); 6240 try writer.writeByte(')'); 6241 if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); 6242 try writer.writeAll(" *)"); 6243 try f.writeCValue(writer, ptr, .Other); 6244 try writer.writeAll(", "); 6245 try element_mat.mat(f, writer); 6246 try writer.print(", {s}, ", .{order}); 6247 try f.object.dg.renderTypeForBuiltinFnName(writer, ty); 6248 try writer.writeAll(", "); 6249 try f.object.dg.renderType(writer, repr_ty); 6250 try writer.writeAll(");\n"); 6251 try element_mat.end(f, inst); 6252 6253 return .none; 6254 } 6255 6256 fn writeSliceOrPtr(f: *Function, writer: anytype, ptr: CValue, ptr_ty: Type) !void { 6257 if (ptr_ty.isSlice()) { 6258 try f.writeCValueMember(writer, ptr, .{ .identifier = "ptr" }); 6259 } else { 6260 try f.writeCValue(writer, ptr, .FunctionArgument); 6261 } 6262 } 6263 6264 fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { 6265 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 6266 const dest_ty = f.air.typeOf(bin_op.lhs); 6267 const dest_slice = try f.resolveInst(bin_op.lhs); 6268 const value = try f.resolveInst(bin_op.rhs); 6269 const elem_ty = f.air.typeOf(bin_op.rhs); 6270 const target = f.object.dg.module.getTarget(); 6271 const elem_abi_size = elem_ty.abiSize(target); 6272 const val_is_undef = if (f.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false; 6273 const writer = f.object.writer(); 6274 6275 if (val_is_undef) { 6276 if (!safety) { 6277 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 6278 return .none; 6279 } 6280 6281 try writer.writeAll("memset("); 6282 switch (dest_ty.ptrSize()) { 6283 .Slice => { 6284 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); 6285 try writer.writeAll(", 0xaa, "); 6286 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); 6287 if (elem_abi_size > 1) { 6288 try writer.print(" * {d});\n", .{elem_abi_size}); 6289 } else { 6290 try writer.writeAll(");\n"); 6291 } 6292 }, 6293 .One => { 6294 const array_ty = dest_ty.childType(); 6295 const len = array_ty.arrayLen() * elem_abi_size; 6296 6297 try f.writeCValue(writer, dest_slice, .FunctionArgument); 6298 try writer.print(", 0xaa, {d});\n", .{len}); 6299 }, 6300 .Many, .C => unreachable, 6301 } 6302 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 6303 return .none; 6304 } 6305 6306 if (elem_abi_size > 1 or dest_ty.isVolatilePtr()) { 6307 // For the assignment in this loop, the array pointer needs to get 6308 // casted to a regular pointer, otherwise an error like this occurs: 6309 // error: array type 'uint32_t[20]' (aka 'unsigned int[20]') is not assignable 6310 var elem_ptr_ty_pl: Type.Payload.ElemType = .{ 6311 .base = .{ .tag = .c_mut_pointer }, 6312 .data = elem_ty, 6313 }; 6314 const elem_ptr_ty = Type.initPayload(&elem_ptr_ty_pl.base); 6315 6316 const index = try f.allocLocal(inst, Type.usize); 6317 6318 try writer.writeAll("for ("); 6319 try f.writeCValue(writer, index, .Other); 6320 try writer.writeAll(" = "); 6321 try f.object.dg.renderValue(writer, Type.usize, Value.zero, .Initializer); 6322 try writer.writeAll("; "); 6323 try f.writeCValue(writer, index, .Other); 6324 try writer.writeAll(" != "); 6325 switch (dest_ty.ptrSize()) { 6326 .Slice => { 6327 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); 6328 }, 6329 .One => { 6330 const array_ty = dest_ty.childType(); 6331 try writer.print("{d}", .{array_ty.arrayLen()}); 6332 }, 6333 .Many, .C => unreachable, 6334 } 6335 try writer.writeAll("; ++"); 6336 try f.writeCValue(writer, index, .Other); 6337 try writer.writeAll(") "); 6338 6339 const a = try Assignment.start(f, writer, elem_ty); 6340 try writer.writeAll("(("); 6341 try f.renderType(writer, elem_ptr_ty); 6342 try writer.writeByte(')'); 6343 try writeSliceOrPtr(f, writer, dest_slice, dest_ty); 6344 try writer.writeAll(")["); 6345 try f.writeCValue(writer, index, .Other); 6346 try writer.writeByte(']'); 6347 try a.assign(f, writer); 6348 try f.writeCValue(writer, value, .Other); 6349 try a.end(f, writer); 6350 6351 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 6352 try freeLocal(f, inst, index.new_local, 0); 6353 6354 return .none; 6355 } 6356 6357 const bitcasted = try bitcast(f, Type.u8, value, elem_ty); 6358 6359 try writer.writeAll("memset("); 6360 switch (dest_ty.ptrSize()) { 6361 .Slice => { 6362 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); 6363 try writer.writeAll(", "); 6364 try f.writeCValue(writer, bitcasted.c_value, .FunctionArgument); 6365 try writer.writeAll(", "); 6366 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); 6367 try writer.writeAll(");\n"); 6368 }, 6369 .One => { 6370 const array_ty = dest_ty.childType(); 6371 const len = array_ty.arrayLen() * elem_abi_size; 6372 6373 try f.writeCValue(writer, dest_slice, .FunctionArgument); 6374 try writer.writeAll(", "); 6375 try f.writeCValue(writer, bitcasted.c_value, .FunctionArgument); 6376 try writer.print(", {d});\n", .{len}); 6377 }, 6378 .Many, .C => unreachable, 6379 } 6380 try bitcasted.free(f); 6381 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 6382 return .none; 6383 } 6384 6385 fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { 6386 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 6387 const dest_ptr = try f.resolveInst(bin_op.lhs); 6388 const src_ptr = try f.resolveInst(bin_op.rhs); 6389 const dest_ty = f.air.typeOf(bin_op.lhs); 6390 const src_ty = f.air.typeOf(bin_op.rhs); 6391 const target = f.object.dg.module.getTarget(); 6392 const writer = f.object.writer(); 6393 6394 try writer.writeAll("memcpy("); 6395 try writeSliceOrPtr(f, writer, dest_ptr, dest_ty); 6396 try writer.writeAll(", "); 6397 try writeSliceOrPtr(f, writer, src_ptr, src_ty); 6398 try writer.writeAll(", "); 6399 switch (dest_ty.ptrSize()) { 6400 .Slice => { 6401 const elem_ty = dest_ty.childType(); 6402 const elem_abi_size = elem_ty.abiSize(target); 6403 try f.writeCValueMember(writer, dest_ptr, .{ .identifier = "len" }); 6404 if (elem_abi_size > 1) { 6405 try writer.print(" * {d});\n", .{elem_abi_size}); 6406 } else { 6407 try writer.writeAll(");\n"); 6408 } 6409 }, 6410 .One => { 6411 const array_ty = dest_ty.childType(); 6412 const elem_ty = array_ty.childType(); 6413 const elem_abi_size = elem_ty.abiSize(target); 6414 const len = array_ty.arrayLen() * elem_abi_size; 6415 try writer.print("{d});\n", .{len}); 6416 }, 6417 .Many, .C => unreachable, 6418 } 6419 6420 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 6421 return .none; 6422 } 6423 6424 fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { 6425 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 6426 const union_ptr = try f.resolveInst(bin_op.lhs); 6427 const new_tag = try f.resolveInst(bin_op.rhs); 6428 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 6429 6430 const target = f.object.dg.module.getTarget(); 6431 const union_ty = f.air.typeOf(bin_op.lhs).childType(); 6432 const layout = union_ty.unionGetLayout(target); 6433 if (layout.tag_size == 0) return .none; 6434 const tag_ty = union_ty.unionTagTypeSafety().?; 6435 6436 const writer = f.object.writer(); 6437 const a = try Assignment.start(f, writer, tag_ty); 6438 try f.writeCValueDerefMember(writer, union_ptr, .{ .identifier = "tag" }); 6439 try a.assign(f, writer); 6440 try f.writeCValue(writer, new_tag, .Other); 6441 try a.end(f, writer); 6442 return .none; 6443 } 6444 6445 fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { 6446 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 6447 6448 const operand = try f.resolveInst(ty_op.operand); 6449 try reap(f, inst, &.{ty_op.operand}); 6450 6451 const union_ty = f.air.typeOf(ty_op.operand); 6452 const target = f.object.dg.module.getTarget(); 6453 const layout = union_ty.unionGetLayout(target); 6454 if (layout.tag_size == 0) return .none; 6455 6456 const inst_ty = f.air.typeOfIndex(inst); 6457 const writer = f.object.writer(); 6458 const local = try f.allocLocal(inst, inst_ty); 6459 const a = try Assignment.start(f, writer, inst_ty); 6460 try f.writeCValue(writer, local, .Other); 6461 try a.assign(f, writer); 6462 try f.writeCValueMember(writer, operand, .{ .identifier = "tag" }); 6463 try a.end(f, writer); 6464 return local; 6465 } 6466 6467 fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { 6468 const un_op = f.air.instructions.items(.data)[inst].un_op; 6469 6470 const inst_ty = f.air.typeOfIndex(inst); 6471 const enum_ty = f.air.typeOf(un_op); 6472 const operand = try f.resolveInst(un_op); 6473 try reap(f, inst, &.{un_op}); 6474 6475 const writer = f.object.writer(); 6476 const local = try f.allocLocal(inst, inst_ty); 6477 try f.writeCValue(writer, local, .Other); 6478 try writer.print(" = {s}(", .{ 6479 try f.getLazyFnName(.{ .tag_name = enum_ty.getOwnerDecl() }, .{ .tag_name = enum_ty }), 6480 }); 6481 try f.writeCValue(writer, operand, .Other); 6482 try writer.writeAll(");\n"); 6483 6484 return local; 6485 } 6486 6487 fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { 6488 const un_op = f.air.instructions.items(.data)[inst].un_op; 6489 6490 const writer = f.object.writer(); 6491 const inst_ty = f.air.typeOfIndex(inst); 6492 const operand = try f.resolveInst(un_op); 6493 try reap(f, inst, &.{un_op}); 6494 const local = try f.allocLocal(inst, inst_ty); 6495 try f.writeCValue(writer, local, .Other); 6496 6497 try writer.writeAll(" = zig_errorName["); 6498 try f.writeCValue(writer, operand, .Other); 6499 try writer.writeAll("];\n"); 6500 return local; 6501 } 6502 6503 fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { 6504 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 6505 6506 const operand = try f.resolveInst(ty_op.operand); 6507 try reap(f, inst, &.{ty_op.operand}); 6508 6509 const inst_ty = f.air.typeOfIndex(inst); 6510 const inst_scalar_ty = inst_ty.scalarType(); 6511 const inst_scalar_cty = try f.typeToIndex(inst_scalar_ty, .complete); 6512 const need_memcpy = f.indexToCType(inst_scalar_cty).tag() == .array; 6513 6514 const writer = f.object.writer(); 6515 const local = try f.allocLocal(inst, inst_ty); 6516 const v = try Vectorize.start(f, inst, writer, inst_ty); 6517 if (need_memcpy) try writer.writeAll("memcpy(&"); 6518 try f.writeCValue(writer, local, .Other); 6519 try v.elem(f, writer); 6520 try writer.writeAll(if (need_memcpy) ", &" else " = "); 6521 try f.writeCValue(writer, operand, .Other); 6522 if (need_memcpy) { 6523 try writer.writeAll(", sizeof("); 6524 try f.renderCType(writer, inst_scalar_cty); 6525 try writer.writeAll("))"); 6526 } 6527 try writer.writeAll(";\n"); 6528 try v.end(f, inst, writer); 6529 6530 return local; 6531 } 6532 6533 fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { 6534 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 6535 const extra = f.air.extraData(Air.Bin, pl_op.payload).data; 6536 6537 const pred = try f.resolveInst(pl_op.operand); 6538 const lhs = try f.resolveInst(extra.lhs); 6539 const rhs = try f.resolveInst(extra.rhs); 6540 try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); 6541 6542 const inst_ty = f.air.typeOfIndex(inst); 6543 6544 const writer = f.object.writer(); 6545 const local = try f.allocLocal(inst, inst_ty); 6546 const v = try Vectorize.start(f, inst, writer, inst_ty); 6547 try f.writeCValue(writer, local, .Other); 6548 try v.elem(f, writer); 6549 try writer.writeAll(" = "); 6550 try f.writeCValue(writer, pred, .Other); 6551 try v.elem(f, writer); 6552 try writer.writeAll(" ? "); 6553 try f.writeCValue(writer, lhs, .Other); 6554 try v.elem(f, writer); 6555 try writer.writeAll(" : "); 6556 try f.writeCValue(writer, rhs, .Other); 6557 try v.elem(f, writer); 6558 try writer.writeAll(";\n"); 6559 try v.end(f, inst, writer); 6560 6561 return local; 6562 } 6563 6564 fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { 6565 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 6566 const extra = f.air.extraData(Air.Shuffle, ty_pl.payload).data; 6567 6568 const mask = f.air.values[extra.mask]; 6569 const lhs = try f.resolveInst(extra.a); 6570 const rhs = try f.resolveInst(extra.b); 6571 6572 const module = f.object.dg.module; 6573 const target = module.getTarget(); 6574 const inst_ty = f.air.typeOfIndex(inst); 6575 6576 const writer = f.object.writer(); 6577 const local = try f.allocLocal(inst, inst_ty); 6578 try reap(f, inst, &.{ extra.a, extra.b }); // local cannot alias operands 6579 for (0..extra.mask_len) |index| { 6580 var dst_pl = Value.Payload.U64{ 6581 .base = .{ .tag = .int_u64 }, 6582 .data = @intCast(u64, index), 6583 }; 6584 6585 try f.writeCValue(writer, local, .Other); 6586 try writer.writeByte('['); 6587 try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&dst_pl.base), .Other); 6588 try writer.writeAll("] = "); 6589 6590 var buf: Value.ElemValueBuffer = undefined; 6591 const mask_elem = mask.elemValueBuffer(module, index, &buf).toSignedInt(target); 6592 var src_pl = Value.Payload.U64{ 6593 .base = .{ .tag = .int_u64 }, 6594 .data = @intCast(u64, mask_elem ^ mask_elem >> 63), 6595 }; 6596 6597 try f.writeCValue(writer, if (mask_elem >= 0) lhs else rhs, .Other); 6598 try writer.writeByte('['); 6599 try f.object.dg.renderValue(writer, Type.usize, Value.initPayload(&src_pl.base), .Other); 6600 try writer.writeAll("];\n"); 6601 } 6602 6603 return local; 6604 } 6605 6606 fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { 6607 const reduce = f.air.instructions.items(.data)[inst].reduce; 6608 6609 const target = f.object.dg.module.getTarget(); 6610 const scalar_ty = f.air.typeOfIndex(inst); 6611 const operand = try f.resolveInst(reduce.operand); 6612 try reap(f, inst, &.{reduce.operand}); 6613 const operand_ty = f.air.typeOf(reduce.operand); 6614 const writer = f.object.writer(); 6615 6616 const use_operator = scalar_ty.bitSize(target) <= 64; 6617 const op: union(enum) { 6618 const Func = struct { operation: []const u8, info: BuiltinInfo = .none }; 6619 float_op: Func, 6620 builtin: Func, 6621 infix: []const u8, 6622 ternary: []const u8, 6623 } = switch (reduce.operation) { 6624 .And => if (use_operator) .{ .infix = " &= " } else .{ .builtin = .{ .operation = "and" } }, 6625 .Or => if (use_operator) .{ .infix = " |= " } else .{ .builtin = .{ .operation = "or" } }, 6626 .Xor => if (use_operator) .{ .infix = " ^= " } else .{ .builtin = .{ .operation = "xor" } }, 6627 .Min => switch (scalar_ty.zigTypeTag()) { 6628 .Int => if (use_operator) .{ .ternary = " < " } else .{ 6629 .builtin = .{ .operation = "min" }, 6630 }, 6631 .Float => .{ .float_op = .{ .operation = "fmin" } }, 6632 else => unreachable, 6633 }, 6634 .Max => switch (scalar_ty.zigTypeTag()) { 6635 .Int => if (use_operator) .{ .ternary = " > " } else .{ 6636 .builtin = .{ .operation = "max" }, 6637 }, 6638 .Float => .{ .float_op = .{ .operation = "fmax" } }, 6639 else => unreachable, 6640 }, 6641 .Add => switch (scalar_ty.zigTypeTag()) { 6642 .Int => if (use_operator) .{ .infix = " += " } else .{ 6643 .builtin = .{ .operation = "addw", .info = .bits }, 6644 }, 6645 .Float => .{ .builtin = .{ .operation = "add" } }, 6646 else => unreachable, 6647 }, 6648 .Mul => switch (scalar_ty.zigTypeTag()) { 6649 .Int => if (use_operator) .{ .infix = " *= " } else .{ 6650 .builtin = .{ .operation = "mulw", .info = .bits }, 6651 }, 6652 .Float => .{ .builtin = .{ .operation = "mul" } }, 6653 else => unreachable, 6654 }, 6655 }; 6656 6657 // Reduce a vector by repeatedly applying a function to produce an 6658 // accumulated result. 6659 // 6660 // Equivalent to: 6661 // reduce: { 6662 // var accum: T = init; 6663 // for (vec) : (elem) { 6664 // accum = func(accum, elem); 6665 // } 6666 // break :reduce accum; 6667 // } 6668 6669 const accum = try f.allocLocal(inst, scalar_ty); 6670 try f.writeCValue(writer, accum, .Other); 6671 try writer.writeAll(" = "); 6672 6673 var arena = std.heap.ArenaAllocator.init(f.object.dg.gpa); 6674 defer arena.deinit(); 6675 6676 const ExpectedContents = union { 6677 u: Value.Payload.U64, 6678 i: Value.Payload.I64, 6679 f16: Value.Payload.Float_16, 6680 f32: Value.Payload.Float_32, 6681 f64: Value.Payload.Float_64, 6682 f80: Value.Payload.Float_80, 6683 f128: Value.Payload.Float_128, 6684 }; 6685 var stack align(@alignOf(ExpectedContents)) = 6686 std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator()); 6687 6688 try f.object.dg.renderValue(writer, scalar_ty, switch (reduce.operation) { 6689 .Or, .Xor, .Add => Value.zero, 6690 .And => switch (scalar_ty.zigTypeTag()) { 6691 .Bool => Value.one, 6692 else => switch (scalar_ty.intInfo(target).signedness) { 6693 .unsigned => try scalar_ty.maxInt(stack.get(), target), 6694 .signed => Value.negative_one, 6695 }, 6696 }, 6697 .Min => switch (scalar_ty.zigTypeTag()) { 6698 .Bool => Value.one, 6699 .Int => try scalar_ty.maxInt(stack.get(), target), 6700 .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target), 6701 else => unreachable, 6702 }, 6703 .Max => switch (scalar_ty.zigTypeTag()) { 6704 .Bool => Value.zero, 6705 .Int => try scalar_ty.minInt(stack.get(), target), 6706 .Float => try Value.floatToValue(std.math.nan(f128), stack.get(), scalar_ty, target), 6707 else => unreachable, 6708 }, 6709 .Mul => Value.one, 6710 }, .Initializer); 6711 try writer.writeAll(";\n"); 6712 6713 const v = try Vectorize.start(f, inst, writer, operand_ty); 6714 try f.writeCValue(writer, accum, .Other); 6715 switch (op) { 6716 .float_op => |func| { 6717 try writer.writeAll(" = zig_libc_name_"); 6718 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 6719 try writer.print("({s})(", .{func.operation}); 6720 try f.writeCValue(writer, accum, .FunctionArgument); 6721 try writer.writeAll(", "); 6722 try f.writeCValue(writer, operand, .Other); 6723 try v.elem(f, writer); 6724 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info); 6725 try writer.writeByte(')'); 6726 }, 6727 .builtin => |func| { 6728 try writer.print(" = zig_{s}_", .{func.operation}); 6729 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 6730 try writer.writeByte('('); 6731 try f.writeCValue(writer, accum, .FunctionArgument); 6732 try writer.writeAll(", "); 6733 try f.writeCValue(writer, operand, .Other); 6734 try v.elem(f, writer); 6735 try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info); 6736 try writer.writeByte(')'); 6737 }, 6738 .infix => |ass| { 6739 try writer.writeAll(ass); 6740 try f.writeCValue(writer, operand, .Other); 6741 try v.elem(f, writer); 6742 }, 6743 .ternary => |cmp| { 6744 try writer.writeAll(" = "); 6745 try f.writeCValue(writer, accum, .Other); 6746 try writer.writeAll(cmp); 6747 try f.writeCValue(writer, operand, .Other); 6748 try v.elem(f, writer); 6749 try writer.writeAll(" ? "); 6750 try f.writeCValue(writer, accum, .Other); 6751 try writer.writeAll(" : "); 6752 try f.writeCValue(writer, operand, .Other); 6753 try v.elem(f, writer); 6754 }, 6755 } 6756 try writer.writeAll(";\n"); 6757 try v.end(f, inst, writer); 6758 6759 return accum; 6760 } 6761 6762 fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { 6763 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 6764 const inst_ty = f.air.typeOfIndex(inst); 6765 const len = @intCast(usize, inst_ty.arrayLen()); 6766 const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]); 6767 const gpa = f.object.dg.gpa; 6768 const resolved_elements = try gpa.alloc(CValue, elements.len); 6769 defer gpa.free(resolved_elements); 6770 for (resolved_elements, elements) |*resolved_element, element| { 6771 resolved_element.* = try f.resolveInst(element); 6772 } 6773 { 6774 var bt = iterateBigTomb(f, inst); 6775 for (elements) |element| { 6776 try bt.feed(element); 6777 } 6778 } 6779 6780 const target = f.object.dg.module.getTarget(); 6781 6782 const writer = f.object.writer(); 6783 const local = try f.allocLocal(inst, inst_ty); 6784 switch (inst_ty.zigTypeTag()) { 6785 .Array, .Vector => { 6786 const elem_ty = inst_ty.childType(); 6787 const a = try Assignment.init(f, elem_ty); 6788 for (resolved_elements, 0..) |element, i| { 6789 try a.restart(f, writer); 6790 try f.writeCValue(writer, local, .Other); 6791 try writer.print("[{d}]", .{i}); 6792 try a.assign(f, writer); 6793 try f.writeCValue(writer, element, .Other); 6794 try a.end(f, writer); 6795 } 6796 if (inst_ty.sentinel()) |sentinel| { 6797 try a.restart(f, writer); 6798 try f.writeCValue(writer, local, .Other); 6799 try writer.print("[{d}]", .{resolved_elements.len}); 6800 try a.assign(f, writer); 6801 try f.object.dg.renderValue(writer, elem_ty, sentinel, .Other); 6802 try a.end(f, writer); 6803 } 6804 }, 6805 .Struct => switch (inst_ty.containerLayout()) { 6806 .Auto, .Extern => for (resolved_elements, 0..) |element, field_i| { 6807 if (inst_ty.structFieldIsComptime(field_i)) continue; 6808 const field_ty = inst_ty.structFieldType(field_i); 6809 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; 6810 6811 const a = try Assignment.start(f, writer, field_ty); 6812 try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple()) 6813 .{ .field = field_i } 6814 else 6815 .{ .identifier = inst_ty.structFieldName(field_i) }); 6816 try a.assign(f, writer); 6817 try f.writeCValue(writer, element, .Other); 6818 try a.end(f, writer); 6819 }, 6820 .Packed => { 6821 try f.writeCValue(writer, local, .Other); 6822 try writer.writeAll(" = "); 6823 const int_info = inst_ty.intInfo(target); 6824 6825 var bit_offset_ty_pl = Type.Payload.Bits{ 6826 .base = .{ .tag = .int_unsigned }, 6827 .data = Type.smallestUnsignedBits(int_info.bits - 1), 6828 }; 6829 const bit_offset_ty = Type.initPayload(&bit_offset_ty_pl.base); 6830 6831 var bit_offset_val_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = 0 }; 6832 const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); 6833 6834 var empty = true; 6835 for (0..elements.len) |field_i| { 6836 if (inst_ty.structFieldIsComptime(field_i)) continue; 6837 const field_ty = inst_ty.structFieldType(field_i); 6838 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; 6839 6840 if (!empty) { 6841 try writer.writeAll("zig_or_"); 6842 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); 6843 try writer.writeByte('('); 6844 } 6845 empty = false; 6846 } 6847 empty = true; 6848 for (resolved_elements, 0..) |element, field_i| { 6849 if (inst_ty.structFieldIsComptime(field_i)) continue; 6850 const field_ty = inst_ty.structFieldType(field_i); 6851 if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; 6852 6853 if (!empty) try writer.writeAll(", "); 6854 // TODO: Skip this entire shift if val is 0? 6855 try writer.writeAll("zig_shlw_"); 6856 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); 6857 try writer.writeByte('('); 6858 6859 if (inst_ty.isAbiInt() and (field_ty.isAbiInt() or field_ty.isPtrAtRuntime())) { 6860 try f.renderIntCast(writer, inst_ty, element, .{}, field_ty, .FunctionArgument); 6861 } else { 6862 try writer.writeByte('('); 6863 try f.renderType(writer, inst_ty); 6864 try writer.writeByte(')'); 6865 if (field_ty.isPtrAtRuntime()) { 6866 try writer.writeByte('('); 6867 try f.renderType(writer, switch (int_info.signedness) { 6868 .unsigned => Type.usize, 6869 .signed => Type.isize, 6870 }); 6871 try writer.writeByte(')'); 6872 } 6873 try f.writeCValue(writer, element, .Other); 6874 } 6875 6876 try writer.writeAll(", "); 6877 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); 6878 try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); 6879 try writer.writeByte(')'); 6880 if (!empty) try writer.writeByte(')'); 6881 6882 bit_offset_val_pl.data += field_ty.bitSize(target); 6883 empty = false; 6884 } 6885 6886 try writer.writeAll(";\n"); 6887 }, 6888 }, 6889 else => unreachable, 6890 } 6891 6892 return local; 6893 } 6894 6895 fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { 6896 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; 6897 const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data; 6898 6899 const union_ty = f.air.typeOfIndex(inst); 6900 const target = f.object.dg.module.getTarget(); 6901 const union_obj = union_ty.cast(Type.Payload.Union).?.data; 6902 const field_name = union_obj.fields.keys()[extra.field_index]; 6903 const payload_ty = f.air.typeOf(extra.init); 6904 const payload = try f.resolveInst(extra.init); 6905 try reap(f, inst, &.{extra.init}); 6906 6907 const writer = f.object.writer(); 6908 const local = try f.allocLocal(inst, union_ty); 6909 if (union_obj.layout == .Packed) { 6910 try f.writeCValue(writer, local, .Other); 6911 try writer.writeAll(" = "); 6912 try f.writeCValue(writer, payload, .Initializer); 6913 try writer.writeAll(";\n"); 6914 return local; 6915 } 6916 6917 const field: CValue = if (union_ty.unionTagTypeSafety()) |tag_ty| field: { 6918 const layout = union_ty.unionGetLayout(target); 6919 if (layout.tag_size != 0) { 6920 const field_index = tag_ty.enumFieldIndex(field_name).?; 6921 6922 var tag_pl: Value.Payload.U32 = .{ 6923 .base = .{ .tag = .enum_field_index }, 6924 .data = @intCast(u32, field_index), 6925 }; 6926 const tag_val = Value.initPayload(&tag_pl.base); 6927 6928 var int_pl: Value.Payload.U64 = undefined; 6929 const int_val = tag_val.enumToInt(tag_ty, &int_pl); 6930 6931 const a = try Assignment.start(f, writer, tag_ty); 6932 try f.writeCValueMember(writer, local, .{ .identifier = "tag" }); 6933 try a.assign(f, writer); 6934 try writer.print("{}", .{try f.fmtIntLiteral(tag_ty, int_val)}); 6935 try a.end(f, writer); 6936 } 6937 break :field .{ .payload_identifier = field_name }; 6938 } else .{ .identifier = field_name }; 6939 6940 const a = try Assignment.start(f, writer, payload_ty); 6941 try f.writeCValueMember(writer, local, field); 6942 try a.assign(f, writer); 6943 try f.writeCValue(writer, payload, .Other); 6944 try a.end(f, writer); 6945 return local; 6946 } 6947 6948 fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { 6949 const prefetch = f.air.instructions.items(.data)[inst].prefetch; 6950 6951 const ptr = try f.resolveInst(prefetch.ptr); 6952 try reap(f, inst, &.{prefetch.ptr}); 6953 6954 const writer = f.object.writer(); 6955 switch (prefetch.cache) { 6956 .data => { 6957 try writer.writeAll("zig_prefetch("); 6958 try f.writeCValue(writer, ptr, .FunctionArgument); 6959 try writer.print(", {d}, {d});\n", .{ @enumToInt(prefetch.rw), prefetch.locality }); 6960 }, 6961 // The available prefetch intrinsics do not accept a cache argument; only 6962 // address, rw, and locality. 6963 .instruction => {}, 6964 } 6965 6966 return .none; 6967 } 6968 6969 fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { 6970 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 6971 6972 const writer = f.object.writer(); 6973 const inst_ty = f.air.typeOfIndex(inst); 6974 const local = try f.allocLocal(inst, inst_ty); 6975 try f.writeCValue(writer, local, .Other); 6976 6977 try writer.writeAll(" = "); 6978 try writer.print("zig_wasm_memory_size({d});\n", .{pl_op.payload}); 6979 6980 return local; 6981 } 6982 6983 fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue { 6984 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 6985 6986 const writer = f.object.writer(); 6987 const inst_ty = f.air.typeOfIndex(inst); 6988 const operand = try f.resolveInst(pl_op.operand); 6989 try reap(f, inst, &.{pl_op.operand}); 6990 const local = try f.allocLocal(inst, inst_ty); 6991 try f.writeCValue(writer, local, .Other); 6992 6993 try writer.writeAll(" = "); 6994 try writer.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload}); 6995 try f.writeCValue(writer, operand, .FunctionArgument); 6996 try writer.writeAll(");\n"); 6997 return local; 6998 } 6999 7000 fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { 7001 const un_op = f.air.instructions.items(.data)[inst].un_op; 7002 7003 const operand = try f.resolveInst(un_op); 7004 try reap(f, inst, &.{un_op}); 7005 7006 const operand_ty = f.air.typeOf(un_op); 7007 const scalar_ty = operand_ty.scalarType(); 7008 7009 const writer = f.object.writer(); 7010 const local = try f.allocLocal(inst, operand_ty); 7011 const v = try Vectorize.start(f, inst, writer, operand_ty); 7012 try f.writeCValue(writer, local, .Other); 7013 try v.elem(f, writer); 7014 try writer.writeAll(" = zig_neg_"); 7015 try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); 7016 try writer.writeByte('('); 7017 try f.writeCValue(writer, operand, .FunctionArgument); 7018 try v.elem(f, writer); 7019 try writer.writeAll(");\n"); 7020 try v.end(f, inst, writer); 7021 7022 return local; 7023 } 7024 7025 fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { 7026 const un_op = f.air.instructions.items(.data)[inst].un_op; 7027 7028 const operand = try f.resolveInst(un_op); 7029 try reap(f, inst, &.{un_op}); 7030 7031 const inst_ty = f.air.typeOfIndex(inst); 7032 const inst_scalar_ty = inst_ty.scalarType(); 7033 7034 const writer = f.object.writer(); 7035 const local = try f.allocLocal(inst, inst_ty); 7036 const v = try Vectorize.start(f, inst, writer, inst_ty); 7037 try f.writeCValue(writer, local, .Other); 7038 try v.elem(f, writer); 7039 try writer.writeAll(" = zig_libc_name_"); 7040 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); 7041 try writer.writeByte('('); 7042 try writer.writeAll(operation); 7043 try writer.writeAll(")("); 7044 try f.writeCValue(writer, operand, .FunctionArgument); 7045 try v.elem(f, writer); 7046 try writer.writeAll(");\n"); 7047 try v.end(f, inst, writer); 7048 7049 return local; 7050 } 7051 7052 fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CValue { 7053 const bin_op = f.air.instructions.items(.data)[inst].bin_op; 7054 7055 const lhs = try f.resolveInst(bin_op.lhs); 7056 const rhs = try f.resolveInst(bin_op.rhs); 7057 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); 7058 7059 const inst_ty = f.air.typeOfIndex(inst); 7060 const inst_scalar_ty = inst_ty.scalarType(); 7061 7062 const writer = f.object.writer(); 7063 const local = try f.allocLocal(inst, inst_ty); 7064 const v = try Vectorize.start(f, inst, writer, inst_ty); 7065 try f.writeCValue(writer, local, .Other); 7066 try v.elem(f, writer); 7067 try writer.writeAll(" = zig_libc_name_"); 7068 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); 7069 try writer.writeByte('('); 7070 try writer.writeAll(operation); 7071 try writer.writeAll(")("); 7072 try f.writeCValue(writer, lhs, .FunctionArgument); 7073 try v.elem(f, writer); 7074 try writer.writeAll(", "); 7075 try f.writeCValue(writer, rhs, .FunctionArgument); 7076 try v.elem(f, writer); 7077 try writer.writeAll(");\n"); 7078 try v.end(f, inst, writer); 7079 7080 return local; 7081 } 7082 7083 fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { 7084 const pl_op = f.air.instructions.items(.data)[inst].pl_op; 7085 const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data; 7086 7087 const mulend1 = try f.resolveInst(bin_op.lhs); 7088 const mulend2 = try f.resolveInst(bin_op.rhs); 7089 const addend = try f.resolveInst(pl_op.operand); 7090 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); 7091 7092 const inst_ty = f.air.typeOfIndex(inst); 7093 const inst_scalar_ty = inst_ty.scalarType(); 7094 7095 const writer = f.object.writer(); 7096 const local = try f.allocLocal(inst, inst_ty); 7097 const v = try Vectorize.start(f, inst, writer, inst_ty); 7098 try f.writeCValue(writer, local, .Other); 7099 try v.elem(f, writer); 7100 try writer.writeAll(" = zig_libc_name_"); 7101 try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); 7102 try writer.writeAll("(fma)("); 7103 try f.writeCValue(writer, mulend1, .FunctionArgument); 7104 try v.elem(f, writer); 7105 try writer.writeAll(", "); 7106 try f.writeCValue(writer, mulend2, .FunctionArgument); 7107 try v.elem(f, writer); 7108 try writer.writeAll(", "); 7109 try f.writeCValue(writer, addend, .FunctionArgument); 7110 try v.elem(f, writer); 7111 try writer.writeAll(");\n"); 7112 try v.end(f, inst, writer); 7113 7114 return local; 7115 } 7116 7117 fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { 7118 const inst_ty = f.air.typeOfIndex(inst); 7119 const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); 7120 const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; 7121 7122 const writer = f.object.writer(); 7123 const local = try f.allocLocal(inst, inst_ty); 7124 try writer.writeAll("va_start(*(va_list *)&"); 7125 try f.writeCValue(writer, local, .Other); 7126 if (param_len > 0) { 7127 try writer.writeAll(", "); 7128 try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); 7129 } 7130 try writer.writeAll(");\n"); 7131 return local; 7132 } 7133 7134 fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { 7135 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 7136 7137 const inst_ty = f.air.typeOfIndex(inst); 7138 const va_list = try f.resolveInst(ty_op.operand); 7139 try reap(f, inst, &.{ty_op.operand}); 7140 7141 const writer = f.object.writer(); 7142 const local = try f.allocLocal(inst, inst_ty); 7143 try f.writeCValue(writer, local, .Other); 7144 try writer.writeAll(" = va_arg(*(va_list *)"); 7145 try f.writeCValue(writer, va_list, .Other); 7146 try writer.writeAll(", "); 7147 try f.renderType(writer, f.air.getRefType(ty_op.ty)); 7148 try writer.writeAll(");\n"); 7149 return local; 7150 } 7151 7152 fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { 7153 const un_op = f.air.instructions.items(.data)[inst].un_op; 7154 7155 const va_list = try f.resolveInst(un_op); 7156 try reap(f, inst, &.{un_op}); 7157 7158 const writer = f.object.writer(); 7159 try writer.writeAll("va_end(*(va_list *)"); 7160 try f.writeCValue(writer, va_list, .Other); 7161 try writer.writeAll(");\n"); 7162 return .none; 7163 } 7164 7165 fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { 7166 const ty_op = f.air.instructions.items(.data)[inst].ty_op; 7167 7168 const inst_ty = f.air.typeOfIndex(inst); 7169 const va_list = try f.resolveInst(ty_op.operand); 7170 try reap(f, inst, &.{ty_op.operand}); 7171 7172 const writer = f.object.writer(); 7173 const local = try f.allocLocal(inst, inst_ty); 7174 try writer.writeAll("va_copy(*(va_list *)&"); 7175 try f.writeCValue(writer, local, .Other); 7176 try writer.writeAll(", *(va_list *)"); 7177 try f.writeCValue(writer, va_list, .Other); 7178 try writer.writeAll(");\n"); 7179 return local; 7180 } 7181 7182 fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { 7183 return switch (order) { 7184 // Note: unordered is actually even less atomic than relaxed 7185 .Unordered, .Monotonic => "memory_order_relaxed", 7186 .Acquire => "memory_order_acquire", 7187 .Release => "memory_order_release", 7188 .AcqRel => "memory_order_acq_rel", 7189 .SeqCst => "memory_order_seq_cst", 7190 }; 7191 } 7192 7193 fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void { 7194 return w.writeAll(toMemoryOrder(order)); 7195 } 7196 7197 fn toCallingConvention(call_conv: std.builtin.CallingConvention) ?[]const u8 { 7198 return switch (call_conv) { 7199 .Stdcall => "stdcall", 7200 .Fastcall => "fastcall", 7201 .Vectorcall => "vectorcall", 7202 else => null, 7203 }; 7204 } 7205 7206 fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 { 7207 return switch (order) { 7208 .Xchg => "xchg", 7209 .Add => "add", 7210 .Sub => "sub", 7211 .And => "and", 7212 .Nand => "nand", 7213 .Or => "or", 7214 .Xor => "xor", 7215 .Max => "max", 7216 .Min => "min", 7217 }; 7218 } 7219 7220 fn IndentWriter(comptime UnderlyingWriter: type) type { 7221 return struct { 7222 const Self = @This(); 7223 pub const Error = UnderlyingWriter.Error; 7224 pub const Writer = std.io.Writer(*Self, Error, write); 7225 7226 pub const indent_delta = 1; 7227 7228 underlying_writer: UnderlyingWriter, 7229 indent_count: usize = 0, 7230 current_line_empty: bool = true, 7231 7232 pub fn writer(self: *Self) Writer { 7233 return .{ .context = self }; 7234 } 7235 7236 pub fn write(self: *Self, bytes: []const u8) Error!usize { 7237 if (bytes.len == 0) return @as(usize, 0); 7238 7239 const current_indent = self.indent_count * Self.indent_delta; 7240 if (self.current_line_empty and current_indent > 0) { 7241 try self.underlying_writer.writeByteNTimes(' ', current_indent); 7242 } 7243 self.current_line_empty = false; 7244 7245 return self.writeNoIndent(bytes); 7246 } 7247 7248 pub fn insertNewline(self: *Self) Error!void { 7249 _ = try self.writeNoIndent("\n"); 7250 } 7251 7252 pub fn pushIndent(self: *Self) void { 7253 self.indent_count += 1; 7254 } 7255 7256 pub fn popIndent(self: *Self) void { 7257 assert(self.indent_count != 0); 7258 self.indent_count -= 1; 7259 } 7260 7261 fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize { 7262 if (bytes.len == 0) return @as(usize, 0); 7263 7264 try self.underlying_writer.writeAll(bytes); 7265 if (bytes[bytes.len - 1] == '\n') { 7266 self.current_line_empty = true; 7267 } 7268 return bytes.len; 7269 } 7270 }; 7271 } 7272 7273 fn toCIntBits(zig_bits: u32) ?u32 { 7274 for (&[_]u8{ 8, 16, 32, 64, 128 }) |c_bits| { 7275 if (zig_bits <= c_bits) { 7276 return c_bits; 7277 } 7278 } 7279 return null; 7280 } 7281 7282 fn signAbbrev(signedness: std.builtin.Signedness) u8 { 7283 return switch (signedness) { 7284 .signed => 'i', 7285 .unsigned => 'u', 7286 }; 7287 } 7288 7289 fn compilerRtAbbrev(ty: Type, target: std.Target) []const u8 { 7290 return if (ty.isInt()) switch (ty.intInfo(target).bits) { 7291 1...32 => "si", 7292 33...64 => "di", 7293 65...128 => "ti", 7294 else => unreachable, 7295 } else if (ty.isRuntimeFloat()) switch (ty.floatBits(target)) { 7296 16 => "hf", 7297 32 => "sf", 7298 64 => "df", 7299 80 => "xf", 7300 128 => "tf", 7301 else => unreachable, 7302 } else unreachable; 7303 } 7304 7305 fn compareOperatorAbbrev(operator: std.math.CompareOperator) []const u8 { 7306 return switch (operator) { 7307 .lt => "lt", 7308 .lte => "le", 7309 .eq => "eq", 7310 .gte => "ge", 7311 .gt => "gt", 7312 .neq => "ne", 7313 }; 7314 } 7315 7316 fn compareOperatorC(operator: std.math.CompareOperator) []const u8 { 7317 return switch (operator) { 7318 .lt => "<", 7319 .lte => "<=", 7320 .eq => "==", 7321 .gte => ">=", 7322 .gt => ">", 7323 .neq => "!=", 7324 }; 7325 } 7326 7327 fn StringLiteral(comptime WriterType: type) type { 7328 // MSVC has a length limit of 16380 per string literal (before concatenation) 7329 const max_char_len = 4; 7330 const max_len = 16380 - max_char_len; 7331 7332 return struct { 7333 cur_len: u64 = 0, 7334 counting_writer: std.io.CountingWriter(WriterType), 7335 7336 pub const Error = WriterType.Error; 7337 7338 const Self = @This(); 7339 7340 pub fn start(self: *Self) Error!void { 7341 const writer = self.counting_writer.writer(); 7342 try writer.writeByte('\"'); 7343 } 7344 7345 pub fn end(self: *Self) Error!void { 7346 const writer = self.counting_writer.writer(); 7347 try writer.writeByte('\"'); 7348 } 7349 7350 fn writeStringLiteralChar(writer: anytype, c: u8) !void { 7351 switch (c) { 7352 7 => try writer.writeAll("\\a"), 7353 8 => try writer.writeAll("\\b"), 7354 '\t' => try writer.writeAll("\\t"), 7355 '\n' => try writer.writeAll("\\n"), 7356 11 => try writer.writeAll("\\v"), 7357 12 => try writer.writeAll("\\f"), 7358 '\r' => try writer.writeAll("\\r"), 7359 '"', '\'', '?', '\\' => try writer.print("\\{c}", .{c}), 7360 else => switch (c) { 7361 ' '...'~' => try writer.writeByte(c), 7362 else => try writer.print("\\{o:0>3}", .{c}), 7363 }, 7364 } 7365 } 7366 7367 pub fn writeChar(self: *Self, c: u8) Error!void { 7368 const writer = self.counting_writer.writer(); 7369 7370 if (self.cur_len == 0 and self.counting_writer.bytes_written > 1) 7371 try writer.writeAll("\"\""); 7372 7373 const len = self.counting_writer.bytes_written; 7374 try writeStringLiteralChar(writer, c); 7375 7376 const char_length = self.counting_writer.bytes_written - len; 7377 assert(char_length <= max_char_len); 7378 self.cur_len += char_length; 7379 7380 if (self.cur_len >= max_len) self.cur_len = 0; 7381 } 7382 }; 7383 } 7384 7385 fn stringLiteral(child_stream: anytype) StringLiteral(@TypeOf(child_stream)) { 7386 return .{ .counting_writer = std.io.countingWriter(child_stream) }; 7387 } 7388 7389 const FormatStringContext = struct { str: []const u8, sentinel: ?u8 }; 7390 fn formatStringLiteral( 7391 data: FormatStringContext, 7392 comptime fmt: []const u8, 7393 _: std.fmt.FormatOptions, 7394 writer: anytype, 7395 ) @TypeOf(writer).Error!void { 7396 if (fmt.len != 1 or fmt[0] != 's') @compileError("Invalid fmt: " ++ fmt); 7397 7398 var literal = stringLiteral(writer); 7399 try literal.start(); 7400 for (data.str) |c| try literal.writeChar(c); 7401 if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel); 7402 try literal.end(); 7403 } 7404 7405 fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(formatStringLiteral) { 7406 return .{ .data = .{ .str = str, .sentinel = sentinel } }; 7407 } 7408 7409 fn undefPattern(comptime IntType: type) IntType { 7410 const int_info = @typeInfo(IntType).Int; 7411 const UnsignedType = std.meta.Int(.unsigned, int_info.bits); 7412 return @bitCast(IntType, @as(UnsignedType, (1 << (int_info.bits | 1)) / 3)); 7413 } 7414 7415 const FormatIntLiteralContext = struct { 7416 dg: *DeclGen, 7417 int_info: std.builtin.Type.Int, 7418 kind: CType.Kind, 7419 cty: CType, 7420 val: Value, 7421 }; 7422 fn formatIntLiteral( 7423 data: FormatIntLiteralContext, 7424 comptime fmt: []const u8, 7425 options: std.fmt.FormatOptions, 7426 writer: anytype, 7427 ) @TypeOf(writer).Error!void { 7428 const target = data.dg.module.getTarget(); 7429 7430 const ExpectedContents = struct { 7431 const base = 10; 7432 const bits = 128; 7433 const limbs_count = BigInt.calcTwosCompLimbCount(bits); 7434 7435 undef_limbs: [limbs_count]BigIntLimb, 7436 wrap_limbs: [limbs_count]BigIntLimb, 7437 to_string_buf: [bits]u8, 7438 to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb, 7439 }; 7440 var stack align(@alignOf(ExpectedContents)) = 7441 std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa); 7442 const allocator = stack.get(); 7443 7444 var undef_limbs: []BigIntLimb = &.{}; 7445 defer allocator.free(undef_limbs); 7446 7447 var int_buf: Value.BigIntSpace = undefined; 7448 const int = if (data.val.isUndefDeep()) blk: { 7449 undef_limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits)); 7450 @memset(undef_limbs, undefPattern(BigIntLimb)); 7451 7452 var undef_int = BigInt.Mutable{ 7453 .limbs = undef_limbs, 7454 .len = undef_limbs.len, 7455 .positive = true, 7456 }; 7457 undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits); 7458 break :blk undef_int.toConst(); 7459 } else data.val.toBigInt(&int_buf, target); 7460 assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits)); 7461 7462 const c_bits = @intCast(usize, data.cty.byteSize(data.dg.ctypes.set, target) * 8); 7463 var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined; 7464 const one = BigInt.Mutable.init(&one_limbs, 1).toConst(); 7465 7466 var wrap = BigInt.Mutable{ 7467 .limbs = try allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)), 7468 .len = undefined, 7469 .positive = undefined, 7470 }; 7471 defer allocator.free(wrap.limbs); 7472 7473 const c_limb_info: struct { 7474 cty: CType, 7475 count: usize, 7476 endian: std.builtin.Endian, 7477 homogeneous: bool, 7478 } = switch (data.cty.tag()) { 7479 else => .{ 7480 .cty = CType.initTag(.void), 7481 .count = 1, 7482 .endian = .Little, 7483 .homogeneous = true, 7484 }, 7485 .zig_u128, .zig_i128 => .{ 7486 .cty = CType.initTag(.uint64_t), 7487 .count = 2, 7488 .endian = .Big, 7489 .homogeneous = false, 7490 }, 7491 .array => info: { 7492 const array_data = data.cty.castTag(.array).?.data; 7493 break :info .{ 7494 .cty = data.dg.indexToCType(array_data.elem_type), 7495 .count = @intCast(usize, array_data.len), 7496 .endian = target.cpu.arch.endian(), 7497 .homogeneous = true, 7498 }; 7499 }, 7500 }; 7501 if (c_limb_info.count == 1) { 7502 if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or 7503 data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits)) 7504 return writer.print("{s}_{s}", .{ 7505 data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{ 7506 if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits, 7507 }), 7508 if (int.positive) "MAX" else "MIN", 7509 }); 7510 7511 if (!int.positive) try writer.writeByte('-'); 7512 try data.cty.renderLiteralPrefix(writer, data.kind); 7513 7514 const style: struct { base: u8, case: std.fmt.Case = undefined } = switch (fmt.len) { 7515 0 => .{ .base = 10 }, 7516 1 => switch (fmt[0]) { 7517 'b' => style: { 7518 try writer.writeAll("0b"); 7519 break :style .{ .base = 2 }; 7520 }, 7521 'o' => style: { 7522 try writer.writeByte('0'); 7523 break :style .{ .base = 8 }; 7524 }, 7525 'd' => .{ .base = 10 }, 7526 'x', 'X' => |base| style: { 7527 try writer.writeAll("0x"); 7528 break :style .{ .base = 16, .case = switch (base) { 7529 'x' => .lower, 7530 'X' => .upper, 7531 else => unreachable, 7532 } }; 7533 }, 7534 else => @compileError("Invalid fmt: " ++ fmt), 7535 }, 7536 else => @compileError("Invalid fmt: " ++ fmt), 7537 }; 7538 7539 const string = try int.abs().toStringAlloc(allocator, style.base, style.case); 7540 defer allocator.free(string); 7541 try writer.writeAll(string); 7542 } else { 7543 try data.cty.renderLiteralPrefix(writer, data.kind); 7544 wrap.convertToTwosComplement(int, data.int_info.signedness, c_bits); 7545 @memset(wrap.limbs[wrap.len..], 0); 7546 wrap.len = wrap.limbs.len; 7547 const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count); 7548 7549 var c_limb_int_info = std.builtin.Type.Int{ 7550 .signedness = undefined, 7551 .bits = @intCast(u16, @divExact(c_bits, c_limb_info.count)), 7552 }; 7553 var c_limb_cty: CType = undefined; 7554 7555 var limb_offset: usize = 0; 7556 const most_significant_limb_i = wrap.len - limbs_per_c_limb; 7557 while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) { 7558 const limb_i = switch (c_limb_info.endian) { 7559 .Little => limb_offset, 7560 .Big => most_significant_limb_i - limb_offset, 7561 }; 7562 var c_limb_mut = BigInt.Mutable{ 7563 .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb], 7564 .len = undefined, 7565 .positive = true, 7566 }; 7567 c_limb_mut.normalize(limbs_per_c_limb); 7568 7569 if (limb_i == most_significant_limb_i and 7570 !c_limb_info.homogeneous and data.int_info.signedness == .signed) 7571 { 7572 // most significant limb is actually signed 7573 c_limb_int_info.signedness = .signed; 7574 c_limb_cty = c_limb_info.cty.toSigned(); 7575 7576 c_limb_mut.positive = wrap.positive; 7577 c_limb_mut.truncate( 7578 c_limb_mut.toConst(), 7579 .signed, 7580 data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb), 7581 ); 7582 } else { 7583 c_limb_int_info.signedness = .unsigned; 7584 c_limb_cty = c_limb_info.cty; 7585 } 7586 var c_limb_val_pl = Value.Payload.BigInt{ 7587 .base = .{ .tag = if (c_limb_mut.positive) .int_big_positive else .int_big_negative }, 7588 .data = c_limb_mut.limbs[0..c_limb_mut.len], 7589 }; 7590 7591 if (limb_offset > 0) try writer.writeAll(", "); 7592 try formatIntLiteral(.{ 7593 .dg = data.dg, 7594 .int_info = c_limb_int_info, 7595 .kind = data.kind, 7596 .cty = c_limb_cty, 7597 .val = Value.initPayload(&c_limb_val_pl.base), 7598 }, fmt, options, writer); 7599 } 7600 } 7601 try data.cty.renderLiteralSuffix(writer); 7602 } 7603 7604 const Materialize = struct { 7605 local: CValue, 7606 7607 pub fn start( 7608 f: *Function, 7609 inst: Air.Inst.Index, 7610 writer: anytype, 7611 ty: Type, 7612 value: CValue, 7613 ) !Materialize { 7614 switch (value) { 7615 .local_ref, .constant, .decl_ref, .undef => { 7616 const local = try f.allocLocal(inst, ty); 7617 7618 const a = try Assignment.start(f, writer, ty); 7619 try f.writeCValue(writer, local, .Other); 7620 try a.assign(f, writer); 7621 try f.writeCValue(writer, value, .Other); 7622 try a.end(f, writer); 7623 7624 return .{ .local = local }; 7625 }, 7626 .new_local => |local| return .{ .local = .{ .local = local } }, 7627 else => return .{ .local = value }, 7628 } 7629 } 7630 7631 pub fn mat(self: Materialize, f: *Function, writer: anytype) !void { 7632 try f.writeCValue(writer, self.local, .Other); 7633 } 7634 7635 pub fn end(self: Materialize, f: *Function, inst: Air.Inst.Index) !void { 7636 switch (self.local) { 7637 .new_local => |local| try freeLocal(f, inst, local, 0), 7638 else => {}, 7639 } 7640 } 7641 }; 7642 7643 const Assignment = struct { 7644 cty: CType.Index, 7645 7646 pub fn init(f: *Function, ty: Type) !Assignment { 7647 return .{ .cty = try f.typeToIndex(ty, .complete) }; 7648 } 7649 7650 pub fn start(f: *Function, writer: anytype, ty: Type) !Assignment { 7651 const self = try init(f, ty); 7652 try self.restart(f, writer); 7653 return self; 7654 } 7655 7656 pub fn restart(self: Assignment, f: *Function, writer: anytype) !void { 7657 switch (self.strategy(f)) { 7658 .assign => {}, 7659 .memcpy => try writer.writeAll("memcpy("), 7660 } 7661 } 7662 7663 pub fn assign(self: Assignment, f: *Function, writer: anytype) !void { 7664 switch (self.strategy(f)) { 7665 .assign => try writer.writeAll(" = "), 7666 .memcpy => try writer.writeAll(", "), 7667 } 7668 } 7669 7670 pub fn end(self: Assignment, f: *Function, writer: anytype) !void { 7671 switch (self.strategy(f)) { 7672 .assign => {}, 7673 .memcpy => { 7674 try writer.writeAll(", sizeof("); 7675 try f.renderCType(writer, self.cty); 7676 try writer.writeAll("))"); 7677 }, 7678 } 7679 try writer.writeAll(";\n"); 7680 } 7681 7682 fn strategy(self: Assignment, f: *Function) enum { assign, memcpy } { 7683 return switch (f.indexToCType(self.cty).tag()) { 7684 else => .assign, 7685 .array, .vector => .memcpy, 7686 }; 7687 } 7688 }; 7689 7690 const Vectorize = struct { 7691 index: CValue = .none, 7692 7693 pub fn start(f: *Function, inst: Air.Inst.Index, writer: anytype, ty: Type) !Vectorize { 7694 return if (ty.zigTypeTag() == .Vector) index: { 7695 var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = ty.vectorLen() }; 7696 7697 const local = try f.allocLocal(inst, Type.usize); 7698 7699 try writer.writeAll("for ("); 7700 try f.writeCValue(writer, local, .Other); 7701 try writer.print(" = {d}; ", .{try f.fmtIntLiteral(Type.usize, Value.zero)}); 7702 try f.writeCValue(writer, local, .Other); 7703 try writer.print(" < {d}; ", .{ 7704 try f.fmtIntLiteral(Type.usize, Value.initPayload(&len_pl.base)), 7705 }); 7706 try f.writeCValue(writer, local, .Other); 7707 try writer.print(" += {d}) {{\n", .{try f.fmtIntLiteral(Type.usize, Value.one)}); 7708 f.object.indent_writer.pushIndent(); 7709 7710 break :index .{ .index = local }; 7711 } else .{}; 7712 } 7713 7714 pub fn elem(self: Vectorize, f: *Function, writer: anytype) !void { 7715 if (self.index != .none) { 7716 try writer.writeByte('['); 7717 try f.writeCValue(writer, self.index, .Other); 7718 try writer.writeByte(']'); 7719 } 7720 } 7721 7722 pub fn end(self: Vectorize, f: *Function, inst: Air.Inst.Index, writer: anytype) !void { 7723 if (self.index != .none) { 7724 f.object.indent_writer.popIndent(); 7725 try writer.writeAll("}\n"); 7726 try freeLocal(f, inst, self.index.new_local, 0); 7727 } 7728 } 7729 }; 7730 7731 const LowerFnRetTyBuffer = struct { 7732 names: [1][]const u8, 7733 types: [1]Type, 7734 values: [1]Value, 7735 payload: Type.Payload.AnonStruct, 7736 }; 7737 fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) Type { 7738 if (ret_ty.zigTypeTag() == .NoReturn) return Type.initTag(.noreturn); 7739 7740 if (lowersToArray(ret_ty, target)) { 7741 buffer.names = [1][]const u8{"array"}; 7742 buffer.types = [1]Type{ret_ty}; 7743 buffer.values = [1]Value{Value.initTag(.unreachable_value)}; 7744 buffer.payload = .{ .data = .{ 7745 .names = &buffer.names, 7746 .types = &buffer.types, 7747 .values = &buffer.values, 7748 } }; 7749 return Type.initPayload(&buffer.payload.base); 7750 } 7751 7752 return if (ret_ty.hasRuntimeBitsIgnoreComptime()) ret_ty else Type.void; 7753 } 7754 7755 fn lowersToArray(ty: Type, target: std.Target) bool { 7756 return switch (ty.zigTypeTag()) { 7757 .Array, .Vector => return true, 7758 else => return ty.isAbiInt() and toCIntBits(@intCast(u32, ty.bitSize(target))) == null, 7759 }; 7760 } 7761 7762 fn reap(f: *Function, inst: Air.Inst.Index, operands: []const Air.Inst.Ref) !void { 7763 assert(operands.len <= Liveness.bpi - 1); 7764 var tomb_bits = f.liveness.getTombBits(inst); 7765 for (operands) |operand| { 7766 const dies = @truncate(u1, tomb_bits) != 0; 7767 tomb_bits >>= 1; 7768 if (!dies) continue; 7769 try die(f, inst, operand); 7770 } 7771 } 7772 7773 fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void { 7774 const ref_inst = Air.refToIndex(ref) orelse return; 7775 const c_value = (f.value_map.fetchRemove(ref_inst) orelse return).value; 7776 if (f.air.instructions.items(.tag)[ref_inst] == .constant) return; 7777 const local_index = switch (c_value) { 7778 .local, .new_local => |l| l, 7779 else => return, 7780 }; 7781 try freeLocal(f, inst, local_index, ref_inst); 7782 } 7783 7784 fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_inst: Air.Inst.Index) !void { 7785 const gpa = f.object.dg.gpa; 7786 const local = &f.locals.items[local_index]; 7787 log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst }); 7788 const gop = try f.free_locals_map.getOrPut(gpa, local.getType()); 7789 if (!gop.found_existing) gop.value_ptr.* = .{}; 7790 if (std.debug.runtime_safety) { 7791 // If this trips, an unfreeable allocation was attempted to be freed. 7792 assert(!f.allocs.contains(local_index)); 7793 } 7794 // If this trips, it means a local is being inserted into the 7795 // free_locals map while it already exists in the map, which is not 7796 // allowed. 7797 try gop.value_ptr.putNoClobber(gpa, local_index, {}); 7798 } 7799 7800 const BigTomb = struct { 7801 f: *Function, 7802 inst: Air.Inst.Index, 7803 lbt: Liveness.BigTomb, 7804 7805 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) !void { 7806 const dies = bt.lbt.feed(); 7807 if (!dies) return; 7808 try die(bt.f, bt.inst, op_ref); 7809 } 7810 }; 7811 7812 fn iterateBigTomb(f: *Function, inst: Air.Inst.Index) BigTomb { 7813 return .{ 7814 .f = f, 7815 .inst = inst, 7816 .lbt = f.liveness.iterateBigTomb(inst), 7817 }; 7818 } 7819 7820 /// A naive clone of this map would create copies of the ArrayList which is 7821 /// stored in the values. This function additionally clones the values. 7822 fn cloneFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) !LocalsMap { 7823 var cloned = try map.clone(gpa); 7824 const values = cloned.values(); 7825 var i: usize = 0; 7826 errdefer { 7827 cloned.deinit(gpa); 7828 while (i > 0) { 7829 i -= 1; 7830 values[i].deinit(gpa); 7831 } 7832 } 7833 while (i < values.len) : (i += 1) { 7834 values[i] = try values[i].clone(gpa); 7835 } 7836 return cloned; 7837 } 7838 7839 fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { 7840 for (map.values()) |*value| { 7841 value.deinit(gpa); 7842 } 7843 map.deinit(gpa); 7844 }