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