diff options
| -rw-r--r-- | lib/std/builtin.zig | 7 | ||||
| -rw-r--r-- | lib/std/builtin/assembly.zig | 1026 | ||||
| -rw-r--r-- | lib/std/zig.zig | 2 | ||||
| -rw-r--r-- | lib/std/zig/Ast.zig | 90 | ||||
| -rw-r--r-- | lib/std/zig/AstGen.zig | 68 | ||||
| -rw-r--r-- | lib/std/zig/AstRlAnnotate.zig | 1 | ||||
| -rw-r--r-- | lib/std/zig/Parse.zig | 31 | ||||
| -rw-r--r-- | lib/std/zig/Zir.zig | 14 | ||||
| -rw-r--r-- | lib/std/zig/ZonGen.zig | 2 | ||||
| -rw-r--r-- | lib/std/zig/render.zig | 160 | ||||
| -rw-r--r-- | src/Air.zig | 19 | ||||
| -rw-r--r-- | src/Air/Liveness.zig | 3 | ||||
| -rw-r--r-- | src/Air/Liveness/Verify.zig | 11 | ||||
| -rw-r--r-- | src/Air/print.zig | 53 | ||||
| -rw-r--r-- | src/Air/types_resolved.zig | 5 | ||||
| -rw-r--r-- | src/InternPool.zig | 6 | ||||
| -rw-r--r-- | src/Sema.zig | 40 | ||||
| -rw-r--r-- | src/Zcu.zig | 26 | ||||
| -rw-r--r-- | src/Zcu/PerThread.zig | 1 | ||||
| -rw-r--r-- | src/arch/riscv64/CodeGen.zig | 44 | ||||
| -rw-r--r-- | src/arch/sparc64/CodeGen.zig | 19 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 64 | ||||
| -rw-r--r-- | src/codegen/c.zig | 43 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 76 | ||||
| -rw-r--r-- | src/codegen/spirv.zig | 18 |
25 files changed, 1576 insertions, 253 deletions
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index e8c90de16e..8f4aefc713 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -1,6 +1,10 @@ //! Types and values provided by the Zig language. const builtin = @import("builtin"); +const std = @import("std.zig"); +const root = @import("root"); + +pub const assembly = @import("builtin/assembly.zig"); /// `explicit_subsystem` is missing when the subsystem is automatically detected, /// so Zig standard library has the subsystem detection logic here. This should generally be @@ -1100,6 +1104,3 @@ pub noinline fn returnError() void { st.instruction_addresses[st.index] = @returnAddress(); st.index += 1; } - -const std = @import("std.zig"); -const root = @import("root"); diff --git a/lib/std/builtin/assembly.zig b/lib/std/builtin/assembly.zig new file mode 100644 index 0000000000..e24082ff86 --- /dev/null +++ b/lib/std/builtin/assembly.zig @@ -0,0 +1,1026 @@ +pub const Clobbers = switch (@import("builtin").cpu.arch) { + .x86, .x86_64 => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + cc: bool = false, + dirflag: bool = false, + eflags: bool = false, + flags: bool = false, + fpcr: bool = false, + fpsr: bool = false, + mxcsr: bool = false, + rflags: bool = false, + + rax: bool = false, + rcx: bool = false, + rdx: bool = false, + rbx: bool = false, + rsp: bool = false, + rbp: bool = false, + rsi: bool = false, + rdi: bool = false, + r8: bool = false, + r9: bool = false, + r10: bool = false, + r11: bool = false, + r12: bool = false, + r13: bool = false, + r14: bool = false, + r15: bool = false, + eax: bool = false, + ecx: bool = false, + edx: bool = false, + ebx: bool = false, + esp: bool = false, + ebp: bool = false, + esi: bool = false, + edi: bool = false, + r8d: bool = false, + r9d: bool = false, + r10d: bool = false, + r11d: bool = false, + r12d: bool = false, + r13d: bool = false, + r14d: bool = false, + r15d: bool = false, + ax: bool = false, + cx: bool = false, + dx: bool = false, + bx: bool = false, + sp: bool = false, + bp: bool = false, + si: bool = false, + di: bool = false, + r8w: bool = false, + r9w: bool = false, + r10w: bool = false, + r11w: bool = false, + r12w: bool = false, + r13w: bool = false, + r14w: bool = false, + r15w: bool = false, + al: bool = false, + cl: bool = false, + dl: bool = false, + bl: bool = false, + spl: bool = false, + bpl: bool = false, + sil: bool = false, + dil: bool = false, + r8b: bool = false, + r9b: bool = false, + r10b: bool = false, + r11b: bool = false, + r12b: bool = false, + r13b: bool = false, + r14b: bool = false, + r15b: bool = false, + ah: bool = false, + ch: bool = false, + dh: bool = false, + bh: bool = false, + zmm0: bool = false, + zmm1: bool = false, + zmm2: bool = false, + zmm3: bool = false, + zmm4: bool = false, + zmm5: bool = false, + zmm6: bool = false, + zmm7: bool = false, + zmm8: bool = false, + zmm9: bool = false, + zmm10: bool = false, + zmm11: bool = false, + zmm12: bool = false, + zmm13: bool = false, + zmm14: bool = false, + zmm15: bool = false, + zmm16: bool = false, + zmm17: bool = false, + zmm18: bool = false, + zmm19: bool = false, + zmm20: bool = false, + zmm21: bool = false, + zmm22: bool = false, + zmm23: bool = false, + zmm24: bool = false, + zmm25: bool = false, + zmm26: bool = false, + zmm27: bool = false, + zmm28: bool = false, + zmm29: bool = false, + zmm30: bool = false, + zmm31: bool = false, + ymm0: bool = false, + ymm1: bool = false, + ymm2: bool = false, + ymm3: bool = false, + ymm4: bool = false, + ymm5: bool = false, + ymm6: bool = false, + ymm7: bool = false, + ymm8: bool = false, + ymm9: bool = false, + ymm10: bool = false, + ymm11: bool = false, + ymm12: bool = false, + ymm13: bool = false, + ymm14: bool = false, + ymm15: bool = false, + ymm16: bool = false, + ymm17: bool = false, + ymm18: bool = false, + ymm19: bool = false, + ymm20: bool = false, + ymm21: bool = false, + ymm22: bool = false, + ymm23: bool = false, + ymm24: bool = false, + ymm25: bool = false, + ymm26: bool = false, + ymm27: bool = false, + ymm28: bool = false, + ymm29: bool = false, + ymm30: bool = false, + ymm31: bool = false, + xmm0: bool = false, + xmm1: bool = false, + xmm2: bool = false, + xmm3: bool = false, + xmm4: bool = false, + xmm5: bool = false, + xmm6: bool = false, + xmm7: bool = false, + xmm8: bool = false, + xmm9: bool = false, + xmm10: bool = false, + xmm11: bool = false, + xmm12: bool = false, + xmm13: bool = false, + xmm14: bool = false, + xmm15: bool = false, + xmm16: bool = false, + xmm17: bool = false, + xmm18: bool = false, + xmm19: bool = false, + xmm20: bool = false, + xmm21: bool = false, + xmm22: bool = false, + xmm23: bool = false, + xmm24: bool = false, + xmm25: bool = false, + xmm26: bool = false, + xmm27: bool = false, + xmm28: bool = false, + xmm29: bool = false, + xmm30: bool = false, + xmm31: bool = false, + mm0: bool = false, + mm1: bool = false, + mm2: bool = false, + mm3: bool = false, + mm4: bool = false, + mm5: bool = false, + mm6: bool = false, + mm7: bool = false, + st0: bool = false, + st1: bool = false, + st2: bool = false, + st3: bool = false, + st4: bool = false, + st5: bool = false, + st6: bool = false, + st7: bool = false, + es: bool = false, + cs: bool = false, + ss: bool = false, + ds: bool = false, + fs: bool = false, + gs: bool = false, + rip: bool = false, + eip: bool = false, + ip: bool = false, + cr0: bool = false, + cr1: bool = false, + cr2: bool = false, + cr3: bool = false, + cr4: bool = false, + cr5: bool = false, + cr6: bool = false, + cr7: bool = false, + cr8: bool = false, + cr9: bool = false, + cr10: bool = false, + cr11: bool = false, + cr12: bool = false, + cr13: bool = false, + cr14: bool = false, + cr15: bool = false, + dr0: bool = false, + dr1: bool = false, + dr2: bool = false, + dr3: bool = false, + dr4: bool = false, + dr5: bool = false, + dr6: bool = false, + dr7: bool = false, + dr8: bool = false, + dr9: bool = false, + dr10: bool = false, + dr11: bool = false, + dr12: bool = false, + dr13: bool = false, + dr14: bool = false, + dr15: bool = false, + }, + .aarch64, .aarch64_be => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + nzcv: bool = false, + + x0: bool = false, + x1: bool = false, + x2: bool = false, + x3: bool = false, + x4: bool = false, + x5: bool = false, + x6: bool = false, + x7: bool = false, + x8: bool = false, + x9: bool = false, + x10: bool = false, + x11: bool = false, + x12: bool = false, + x13: bool = false, + x14: bool = false, + x15: bool = false, + x16: bool = false, + x17: bool = false, + x18: bool = false, + x19: bool = false, + x20: bool = false, + x21: bool = false, + x22: bool = false, + x23: bool = false, + x24: bool = false, + x25: bool = false, + x26: bool = false, + x27: bool = false, + x28: bool = false, + x29: bool = false, + x30: bool = false, + + w0: bool = false, + w1: bool = false, + w2: bool = false, + w3: bool = false, + w4: bool = false, + w5: bool = false, + w6: bool = false, + w7: bool = false, + w8: bool = false, + w9: bool = false, + w10: bool = false, + w11: bool = false, + w12: bool = false, + w13: bool = false, + w14: bool = false, + w15: bool = false, + w16: bool = false, + w17: bool = false, + w18: bool = false, + w19: bool = false, + w20: bool = false, + w21: bool = false, + w22: bool = false, + w23: bool = false, + w24: bool = false, + w25: bool = false, + w26: bool = false, + w27: bool = false, + w28: bool = false, + w29: bool = false, + w30: bool = false, + + lr: bool = false, + sp: bool = false, + wsp: bool = false, + fpcr: bool = false, + fpmr: bool = false, + fpsr: bool = false, + ffr: bool = false, + + p0: bool = false, + p1: bool = false, + p2: bool = false, + p3: bool = false, + p4: bool = false, + p5: bool = false, + p6: bool = false, + p7: bool = false, + p8: bool = false, + p9: bool = false, + p10: bool = false, + p11: bool = false, + p12: bool = false, + p13: bool = false, + p14: bool = false, + p15: bool = false, + + z0: bool = false, + z1: bool = false, + z2: bool = false, + z3: bool = false, + z4: bool = false, + z5: bool = false, + z6: bool = false, + z7: bool = false, + z8: bool = false, + z9: bool = false, + z10: bool = false, + z11: bool = false, + z12: bool = false, + z13: bool = false, + z14: bool = false, + z15: bool = false, + z16: bool = false, + z17: bool = false, + z18: bool = false, + z19: bool = false, + z20: bool = false, + z21: bool = false, + z22: bool = false, + z23: bool = false, + z24: bool = false, + z25: bool = false, + z26: bool = false, + z27: bool = false, + z28: bool = false, + z29: bool = false, + z30: bool = false, + z31: bool = false, + + v0: bool = false, + v1: bool = false, + v2: bool = false, + v3: bool = false, + v4: bool = false, + v5: bool = false, + v6: bool = false, + v7: bool = false, + v8: bool = false, + v9: bool = false, + v10: bool = false, + v11: bool = false, + v12: bool = false, + v13: bool = false, + v14: bool = false, + v15: bool = false, + v16: bool = false, + v17: bool = false, + v18: bool = false, + v19: bool = false, + v20: bool = false, + v21: bool = false, + v22: bool = false, + v23: bool = false, + v24: bool = false, + v25: bool = false, + v26: bool = false, + v27: bool = false, + v28: bool = false, + v29: bool = false, + v30: bool = false, + v31: bool = false, + + d0: bool = false, + d1: bool = false, + d2: bool = false, + d3: bool = false, + d4: bool = false, + d5: bool = false, + d6: bool = false, + d7: bool = false, + d8: bool = false, + d9: bool = false, + d10: bool = false, + d11: bool = false, + d12: bool = false, + d13: bool = false, + d14: bool = false, + d15: bool = false, + d16: bool = false, + d17: bool = false, + d18: bool = false, + d19: bool = false, + d20: bool = false, + d21: bool = false, + d22: bool = false, + d23: bool = false, + d24: bool = false, + d25: bool = false, + d26: bool = false, + d27: bool = false, + d28: bool = false, + d29: bool = false, + d30: bool = false, + d31: bool = false, + + s0: bool = false, + s1: bool = false, + s2: bool = false, + s3: bool = false, + s4: bool = false, + s5: bool = false, + s6: bool = false, + s7: bool = false, + s8: bool = false, + s9: bool = false, + s10: bool = false, + s11: bool = false, + s12: bool = false, + s13: bool = false, + s14: bool = false, + s15: bool = false, + s16: bool = false, + s17: bool = false, + s18: bool = false, + s19: bool = false, + s20: bool = false, + s21: bool = false, + s22: bool = false, + s23: bool = false, + s24: bool = false, + s25: bool = false, + s26: bool = false, + s27: bool = false, + s28: bool = false, + s29: bool = false, + s30: bool = false, + s31: bool = false, + + h0: bool = false, + h1: bool = false, + h2: bool = false, + h3: bool = false, + h4: bool = false, + h5: bool = false, + h6: bool = false, + h7: bool = false, + h8: bool = false, + h9: bool = false, + h10: bool = false, + h11: bool = false, + h12: bool = false, + h13: bool = false, + h14: bool = false, + h15: bool = false, + h16: bool = false, + h17: bool = false, + h18: bool = false, + h19: bool = false, + h20: bool = false, + h21: bool = false, + h22: bool = false, + h23: bool = false, + h24: bool = false, + h25: bool = false, + h26: bool = false, + h27: bool = false, + h28: bool = false, + h29: bool = false, + h30: bool = false, + h31: bool = false, + + b0: bool = false, + b1: bool = false, + b2: bool = false, + b3: bool = false, + b4: bool = false, + b5: bool = false, + b6: bool = false, + b7: bool = false, + b8: bool = false, + b9: bool = false, + b10: bool = false, + b11: bool = false, + b12: bool = false, + b13: bool = false, + b14: bool = false, + b15: bool = false, + b16: bool = false, + b17: bool = false, + b18: bool = false, + b19: bool = false, + b20: bool = false, + b21: bool = false, + b22: bool = false, + b23: bool = false, + b24: bool = false, + b25: bool = false, + b26: bool = false, + b27: bool = false, + b28: bool = false, + b29: bool = false, + b30: bool = false, + b31: bool = false, + + za0: bool = false, + za1: bool = false, + za2: bool = false, + za3: bool = false, + za4: bool = false, + za5: bool = false, + za6: bool = false, + za7: bool = false, + za8: bool = false, + za9: bool = false, + za10: bool = false, + za11: bool = false, + za12: bool = false, + za13: bool = false, + za14: bool = false, + za15: bool = false, + + zt0: bool = false, + }, + .arm, .armeb => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + apsr: bool = false, + cpsr: bool = false, + spsr: bool = false, + r0: bool = false, + r1: bool = false, + r2: bool = false, + r3: bool = false, + r4: bool = false, + r5: bool = false, + r6: bool = false, + r7: bool = false, + r8: bool = false, + r9: bool = false, + r10: bool = false, + r11: bool = false, + r12: bool = false, + r13: bool = false, + r14: bool = false, + r15: bool = false, + + fpscr: bool = false, + vpr: bool = false, + + d0: bool = false, + d1: bool = false, + d2: bool = false, + d3: bool = false, + d4: bool = false, + d5: bool = false, + d6: bool = false, + d7: bool = false, + d8: bool = false, + d9: bool = false, + d10: bool = false, + d11: bool = false, + d12: bool = false, + d13: bool = false, + d14: bool = false, + d15: bool = false, + d16: bool = false, + d17: bool = false, + d18: bool = false, + d19: bool = false, + d20: bool = false, + d21: bool = false, + d22: bool = false, + d23: bool = false, + d24: bool = false, + d25: bool = false, + d26: bool = false, + d27: bool = false, + d28: bool = false, + d29: bool = false, + d30: bool = false, + d31: bool = false, + + s0: bool = false, + s1: bool = false, + s2: bool = false, + s3: bool = false, + s4: bool = false, + s5: bool = false, + s6: bool = false, + s7: bool = false, + s8: bool = false, + s9: bool = false, + s10: bool = false, + s11: bool = false, + s12: bool = false, + s13: bool = false, + s14: bool = false, + s15: bool = false, + s16: bool = false, + s17: bool = false, + s18: bool = false, + s19: bool = false, + s20: bool = false, + s21: bool = false, + s22: bool = false, + s23: bool = false, + s24: bool = false, + s25: bool = false, + s26: bool = false, + s27: bool = false, + s28: bool = false, + s29: bool = false, + s30: bool = false, + s31: bool = false, + + q0: bool = false, + q1: bool = false, + q2: bool = false, + q3: bool = false, + q4: bool = false, + q5: bool = false, + q6: bool = false, + q7: bool = false, + q8: bool = false, + q9: bool = false, + q10: bool = false, + q11: bool = false, + q12: bool = false, + q13: bool = false, + q14: bool = false, + q15: bool = false, + }, + .riscv32, .riscv64 => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + ssp: bool = false, + + x1: bool = false, + x2: bool = false, + x3: bool = false, + x4: bool = false, + x5: bool = false, + x6: bool = false, + x7: bool = false, + x8: bool = false, + x9: bool = false, + x10: bool = false, + x11: bool = false, + x12: bool = false, + x13: bool = false, + x14: bool = false, + x15: bool = false, + x16: bool = false, + x17: bool = false, + x18: bool = false, + x19: bool = false, + x20: bool = false, + x21: bool = false, + x22: bool = false, + x23: bool = false, + x24: bool = false, + x25: bool = false, + x26: bool = false, + x27: bool = false, + x28: bool = false, + x29: bool = false, + x30: bool = false, + x31: bool = false, + + fflags: bool = false, + frm: bool = false, + + f0: bool = false, + f1: bool = false, + f2: bool = false, + f3: bool = false, + f4: bool = false, + f5: bool = false, + f6: bool = false, + f7: bool = false, + f8: bool = false, + f9: bool = false, + f10: bool = false, + f11: bool = false, + f12: bool = false, + f13: bool = false, + f14: bool = false, + f15: bool = false, + f16: bool = false, + f17: bool = false, + f18: bool = false, + f19: bool = false, + f20: bool = false, + f21: bool = false, + f22: bool = false, + f23: bool = false, + f24: bool = false, + f25: bool = false, + f26: bool = false, + f27: bool = false, + f28: bool = false, + f29: bool = false, + f30: bool = false, + f31: bool = false, + + vtype: bool = false, + vl: bool = false, + vxsat: bool = false, + vxrm: bool = false, + vcsr: bool = false, + + v0: bool = false, + v1: bool = false, + v2: bool = false, + v3: bool = false, + v4: bool = false, + v5: bool = false, + v6: bool = false, + v7: bool = false, + v8: bool = false, + v9: bool = false, + v10: bool = false, + v11: bool = false, + v12: bool = false, + v13: bool = false, + v14: bool = false, + v15: bool = false, + v16: bool = false, + v17: bool = false, + v18: bool = false, + v19: bool = false, + v20: bool = false, + v21: bool = false, + v22: bool = false, + v23: bool = false, + v24: bool = false, + v25: bool = false, + v26: bool = false, + v27: bool = false, + v28: bool = false, + v29: bool = false, + v30: bool = false, + v31: bool = false, + }, + .xcore => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + r0: bool = false, + r1: bool = false, + r2: bool = false, + r3: bool = false, + r4: bool = false, + r5: bool = false, + r6: bool = false, + r7: bool = false, + r8: bool = false, + r9: bool = false, + r10: bool = false, + r11: bool = false, + + cp: bool = false, + dp: bool = false, + sp: bool = false, + lr: bool = false, + sr: bool = false, + }, + .xtensa => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + sar: bool = false, + lbeg: bool = false, + lend: bool = false, + lcount: bool = false, + atomctl: bool = false, + scompare1: bool = false, + threadptr: bool = false, + litbase: bool = false, + windowbase: bool = false, + windowstart: bool = false, + ps: bool = false, + + a0: bool = false, + a1: bool = false, + a2: bool = false, + a3: bool = false, + a4: bool = false, + a5: bool = false, + a6: bool = false, + a7: bool = false, + a8: bool = false, + a9: bool = false, + a10: bool = false, + a11: bool = false, + a12: bool = false, + a13: bool = false, + a14: bool = false, + a15: bool = false, + + b0: bool = false, + b1: bool = false, + b2: bool = false, + b3: bool = false, + b4: bool = false, + b5: bool = false, + b6: bool = false, + b7: bool = false, + b8: bool = false, + b9: bool = false, + b10: bool = false, + b11: bool = false, + b12: bool = false, + b13: bool = false, + b14: bool = false, + b15: bool = false, + + br: bool = false, + acchi: bool = false, + acclo: bool = false, + m0: bool = false, + m1: bool = false, + m2: bool = false, + m3: bool = false, + fcr: bool = false, + fsr: bool = false, + + f0: bool = false, + f1: bool = false, + f2: bool = false, + f3: bool = false, + f4: bool = false, + f5: bool = false, + f6: bool = false, + f7: bool = false, + f8: bool = false, + f9: bool = false, + f10: bool = false, + f11: bool = false, + f12: bool = false, + f13: bool = false, + f14: bool = false, + f15: bool = false, + }, + .lanai => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + /// Condition flags which aren't accessible outside of conditional execution. + sw: bool = false, + + r3: bool = false, + r4: bool = false, + r5: bool = false, + r6: bool = false, + r7: bool = false, + r8: bool = false, + r9: bool = false, + r10: bool = false, + r11: bool = false, + r12: bool = false, + r13: bool = false, + r14: bool = false, + r15: bool = false, + r16: bool = false, + r17: bool = false, + r18: bool = false, + r19: bool = false, + r20: bool = false, + r21: bool = false, + r22: bool = false, + r23: bool = false, + r24: bool = false, + r25: bool = false, + r26: bool = false, + r27: bool = false, + r28: bool = false, + r29: bool = false, + r30: bool = false, + r31: bool = false, + }, + .avr => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + flags: bool = false, + r0: bool = false, + r1: bool = false, + r2: bool = false, + r3: bool = false, + r4: bool = false, + r5: bool = false, + r6: bool = false, + r7: bool = false, + r8: bool = false, + r9: bool = false, + r10: bool = false, + r11: bool = false, + r12: bool = false, + r13: bool = false, + r14: bool = false, + r15: bool = false, + r16: bool = false, + r17: bool = false, + r18: bool = false, + r19: bool = false, + r20: bool = false, + r21: bool = false, + r22: bool = false, + r23: bool = false, + r24: bool = false, + r25: bool = false, + r26: bool = false, + r27: bool = false, + r28: bool = false, + r29: bool = false, + r30: bool = false, + r31: bool = false, + }, + .msp430 => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + r0: bool = false, + r1: bool = false, + r2: bool = false, + + r4: bool = false, + r5: bool = false, + r6: bool = false, + r7: bool = false, + r8: bool = false, + r9: bool = false, + r10: bool = false, + r11: bool = false, + r12: bool = false, + r13: bool = false, + r14: bool = false, + r15: bool = false, + }, + .m68k => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + ccr: bool = false, + + d0: bool = false, + d1: bool = false, + d2: bool = false, + d3: bool = false, + d4: bool = false, + d5: bool = false, + d6: bool = false, + d7: bool = false, + + a0: bool = false, + a1: bool = false, + a2: bool = false, + a3: bool = false, + a4: bool = false, + a5: bool = false, + a6: bool = false, + a7: bool = false, + + macsr: bool = false, + acc: bool = false, + + acc0: bool = false, + acc1: bool = false, + acc2: bool = false, + acc3: bool = false, + + mask: bool = false, + fpcr: bool = false, + fpsr: bool = false, + + fp0: bool = false, + fp1: bool = false, + fp2: bool = false, + fp3: bool = false, + fp4: bool = false, + fp5: bool = false, + fp6: bool = false, + fp7: bool = false, + }, + else => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + }, +}; diff --git a/lib/std/zig.zig b/lib/std/zig.zig index fd22da91db..ef1f953209 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -740,6 +740,7 @@ pub const SimpleComptimeReason = enum(u32) { generic_call_target, wasm_memory_index, work_group_dim_index, + clobber, // Evaluating at comptime because types must be comptime-known. // Reasons other than `.type` are just more specific messages. @@ -820,6 +821,7 @@ pub const SimpleComptimeReason = enum(u32) { .generic_call_target => "generic function being called must be comptime-known", .wasm_memory_index => "wasm memory index must be comptime-known", .work_group_dim_index => "work group dimension index must be comptime-known", + .clobber => "clobber must be comptime-known", .type => "types must be comptime-known", .array_sentinel => "array sentinel value must be comptime-known", diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index ecdc7a8a4e..aad83f1c63 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -634,6 +634,7 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex { .@"nosuspend", .asm_simple, .@"asm", + .asm_legacy, .array_type, .array_type_sentinel, .error_value, @@ -1047,6 +1048,11 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex { n = @enumFromInt(tree.extra_data[@intFromEnum(members.end) - 1]); // last parameter } }, + .asm_legacy => { + _, const extra_index = tree.nodeData(n).node_and_extra; + const extra = tree.extraData(extra_index, Node.AsmLegacy); + return extra.rparen + end_offset; + }, .@"asm" => { _, const extra_index = tree.nodeData(n).node_and_extra; const extra = tree.extraData(extra_index, Node.Asm); @@ -1885,6 +1891,19 @@ pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm { .template = template, .items = &.{}, .rparen = rparen, + .clobbers = .none, + }); +} + +pub fn asmLegacy(tree: Ast, node: Node.Index) full.AsmLegacy { + const template, const extra_index = tree.nodeData(node).node_and_extra; + const extra = tree.extraData(extra_index, Node.AsmLegacy); + const items = tree.extraDataSlice(.{ .start = extra.items_start, .end = extra.items_end }, Node.Index); + return tree.legacyAsmComponents(.{ + .asm_token = tree.nodeMainToken(node), + .template = template, + .items = items, + .rparen = extra.rparen, }); } @@ -1896,6 +1915,7 @@ pub fn asmFull(tree: Ast, node: Node.Index) full.Asm { .asm_token = tree.nodeMainToken(node), .template = template, .items = items, + .clobbers = extra.clobbers, .rparen = extra.rparen, }); } @@ -2192,8 +2212,8 @@ fn fullSwitchCaseComponents(tree: Ast, info: full.SwitchCase.Components, node: N return result; } -fn fullAsmComponents(tree: Ast, info: full.Asm.Components) full.Asm { - var result: full.Asm = .{ +fn legacyAsmComponents(tree: Ast, info: full.AsmLegacy.Components) full.AsmLegacy { + var result: full.AsmLegacy = .{ .ast = info, .volatile_token = null, .inputs = &.{}, @@ -2253,6 +2273,29 @@ fn fullAsmComponents(tree: Ast, info: full.Asm.Components) full.Asm { return result; } +fn fullAsmComponents(tree: Ast, info: full.Asm.Components) full.Asm { + var result: full.Asm = .{ + .ast = info, + .volatile_token = null, + .inputs = &.{}, + .outputs = &.{}, + }; + if (tree.tokenTag(info.asm_token + 1) == .keyword_volatile) { + result.volatile_token = info.asm_token + 1; + } + const outputs_end: usize = for (info.items, 0..) |item, i| { + switch (tree.nodeTag(item)) { + .asm_output => continue, + else => break i, + } + } else info.items.len; + + result.outputs = info.items[0..outputs_end]; + result.inputs = info.items[outputs_end..]; + + return result; +} + fn fullWhileComponents(tree: Ast, info: full.While.Components) full.While { var result: full.While = .{ .ast = info, @@ -2447,6 +2490,14 @@ pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm { }; } +/// To be deleted after 0.15.0 is tagged +pub fn legacyAsm(tree: Ast, node: Node.Index) ?full.AsmLegacy { + return switch (tree.nodeTag(node)) { + .asm_legacy => tree.asmLegacy(node), + else => null, + }; +} + pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call { return switch (tree.nodeTag(node)) { .call, .call_comma => tree.callFull(node), @@ -2829,6 +2880,21 @@ pub const full = struct { pub const Asm = struct { ast: Components, volatile_token: ?TokenIndex, + outputs: []const Node.Index, + inputs: []const Node.Index, + + pub const Components = struct { + asm_token: TokenIndex, + template: Node.Index, + items: []const Node.Index, + clobbers: Node.OptionalIndex, + rparen: TokenIndex, + }; + }; + + pub const AsmLegacy = struct { + ast: Components, + volatile_token: ?TokenIndex, first_clobber: ?TokenIndex, outputs: []const Node.Index, inputs: []const Node.Index, @@ -3833,15 +3899,22 @@ pub const Node = struct { /// Same as `block` except there is known to be a trailing comma before /// the final rbrace. block_semicolon, - /// `asm(lhs)`. + /// `asm(a)`. /// - /// rhs is a `Token.Index` to the `)` token. /// The `main_token` field is the `asm` token. asm_simple, /// `asm(lhs, a)`. /// /// The `data` field is a `.node_and_extra`: /// 1. a `Node.Index` to lhs. + /// 2. a `ExtraIndex` to `AsmLegacy`. + /// + /// The `main_token` field is the `asm` token. + asm_legacy, + /// `asm(a, b)`. + /// + /// The `data` field is a `.node_and_extra`: + /// 1. a `Node.Index` to a. /// 2. a `ExtraIndex` to `Asm`. /// /// The `main_token` field is the `asm` token. @@ -4014,9 +4087,18 @@ pub const Node = struct { callconv_expr: OptionalIndex, }; + /// To be removed after 0.15.0 is tagged + pub const AsmLegacy = struct { + items_start: ExtraIndex, + items_end: ExtraIndex, + /// Needed to make lastToken() work. + rparen: TokenIndex, + }; + pub const Asm = struct { items_start: ExtraIndex, items_end: ExtraIndex, + clobbers: OptionalIndex, /// Needed to make lastToken() work. rparen: TokenIndex, }; diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index e2ec0e9eb6..175403b295 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -505,6 +505,7 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins .bool_or, .@"asm", .asm_simple, + .asm_legacy, .string_literal, .number_literal, .call, @@ -811,6 +812,12 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .@"asm", => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?), + .asm_legacy => { + return astgen.failNodeNotes(node, "legacy asm clobbers syntax", .{}, &[_]u32{ + try astgen.errNoteNode(node, "use 'zig fmt' to auto-upgrade", .{}), + }); + }, + .string_literal => return stringLiteral(gz, ri, node), .multiline_string_literal => return multilineStringLiteral(gz, ri, node), @@ -8774,7 +8781,7 @@ fn asmExpr( if (is_container_asm) { if (full.volatile_token) |t| return astgen.failTok(t, "volatile is meaningless on global assembly", .{}); - if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null) + if (full.outputs.len != 0 or full.inputs.len != 0 or full.ast.clobbers != .none) return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{}); } else { if (full.outputs.len == 0 and full.volatile_token == null) { @@ -8839,32 +8846,12 @@ fn asmExpr( }; } - var clobbers_buffer: [63]u32 = undefined; - var clobber_i: usize = 0; - if (full.first_clobber) |first_clobber| clobbers: { - // asm ("foo" ::: "a", "b") - // asm ("foo" ::: "a", "b",) - var tok_i = first_clobber; - while (true) : (tok_i += 1) { - if (clobber_i >= clobbers_buffer.len) { - return astgen.failTok(tok_i, "too many asm clobbers", .{}); - } - clobbers_buffer[clobber_i] = @intFromEnum((try astgen.strLitAsString(tok_i)).index); - clobber_i += 1; - tok_i += 1; - switch (tree.tokenTag(tok_i)) { - .r_paren => break :clobbers, - .comma => { - if (tree.tokenTag(tok_i + 1) == .r_paren) { - break :clobbers; - } else { - continue; - } - }, - else => unreachable, - } - } - } + const clobbers: Zir.Inst.Ref = if (full.ast.clobbers.unwrap()) |clobbers_node| + try comptimeExpr(gz, scope, .{ .rl = .{ + .coerced_ty = try gz.addBuiltinValue(clobbers_node, .clobbers), + } }, clobbers_node, .clobber) + else + .none; const result = try gz.addAsm(.{ .tag = tag_and_tmpl.tag, @@ -8874,7 +8861,7 @@ fn asmExpr( .output_type_bits = output_type_bits, .outputs = outputs, .inputs = inputs, - .clobbers = clobbers_buffer[0..clobber_i], + .clobbers = clobbers, }); return rvalue(gz, ri, result, node); } @@ -10332,6 +10319,7 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev .@"asm", .asm_simple, + .asm_legacy, .identifier, .field_access, .deref, @@ -10575,6 +10563,7 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In .tagged_union_enum_tag_trailing, .@"asm", .asm_simple, + .asm_legacy, .add, .add_wrap, .add_sat, @@ -10813,6 +10802,7 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool { .tagged_union_enum_tag_trailing, .@"asm", .asm_simple, + .asm_legacy, .add, .add_wrap, .add_sat, @@ -12806,7 +12796,7 @@ const GenZir = struct { is_volatile: bool, outputs: []const Zir.Inst.Asm.Output, inputs: []const Zir.Inst.Asm.Input, - clobbers: []const u32, + clobbers: Zir.Inst.Ref, }, ) !Zir.Inst.Ref { const astgen = gz.astgen; @@ -12816,13 +12806,13 @@ const GenZir = struct { try astgen.instructions.ensureUnusedCapacity(gpa, 1); try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).@"struct".fields.len + args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".fields.len + - args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".fields.len + - args.clobbers.len); + args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".fields.len); const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{ .src_node = gz.nodeIndexToRelative(args.node), .asm_source = args.asm_source, .output_type_bits = args.output_type_bits, + .clobbers = args.clobbers, }); for (args.outputs) |output| { _ = gz.astgen.addExtraAssumeCapacity(output); @@ -12830,23 +12820,19 @@ const GenZir = struct { for (args.inputs) |input| { _ = gz.astgen.addExtraAssumeCapacity(input); } - gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers); - // * 0b00000000_0000XXXX - `outputs_len`. - // * 0b0000000X_XXXX0000 - `inputs_len`. - // * 0b0XXXXXX0_00000000 - `clobbers_len`. - // * 0bX0000000_00000000 - is volatile - const small: u16 = @as(u16, @as(u4, @intCast(args.outputs.len))) << 0 | - @as(u16, @as(u5, @intCast(args.inputs.len))) << 4 | - @as(u16, @as(u6, @intCast(args.clobbers.len))) << 9 | - @as(u16, @intFromBool(args.is_volatile)) << 15; + const small: Zir.Inst.Asm.Small = .{ + .outputs_len = @intCast(args.outputs.len), + .inputs_len = @intCast(args.inputs.len), + .is_volatile = args.is_volatile, + }; const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); astgen.instructions.appendAssumeCapacity(.{ .tag = .extended, .data = .{ .extended = .{ .opcode = args.tag, - .small = small, + .small = @bitCast(small), .operand = payload_index, } }, }); diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig index 52900224cc..cf0699cc01 100644 --- a/lib/std/zig/AstRlAnnotate.zig +++ b/lib/std/zig/AstRlAnnotate.zig @@ -310,6 +310,7 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI .unreachable_literal, .asm_simple, .@"asm", + .asm_legacy, .enum_literal, .error_value, .anyframe_literal, diff --git a/lib/std/zig/Parse.zig b/lib/std/zig/Parse.zig index fc8b61a403..428442c825 100644 --- a/lib/std/zig/Parse.zig +++ b/lib/std/zig/Parse.zig @@ -2801,7 +2801,7 @@ fn expectSwitchSuffix(p: *Parse, main_token: TokenIndex) !Node.Index { /// /// AsmInput <- COLON AsmInputList AsmClobbers? /// -/// AsmClobbers <- COLON StringList +/// AsmClobbers <- COLON Expr /// /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? /// @@ -2841,7 +2841,8 @@ fn expectAsmExpr(p: *Parse) !Node.Index { else => try p.warnExpected(.comma), } } - if (p.eatToken(.colon)) |_| { + + const clobbers: Node.OptionalIndex = if (p.eatToken(.colon)) |_| clobbers: { while (true) { const input_item = try p.parseAsmInputItem() orelse break; try p.scratch.append(p.gpa, input_item); @@ -2853,7 +2854,11 @@ fn expectAsmExpr(p: *Parse) !Node.Index { else => try p.warnExpected(.comma), } } - if (p.eatToken(.colon)) |_| { + + _ = p.eatToken(.colon) orelse break :clobbers .none; + + // For automatic upgrades; delete after 0.15.0 released. + if (p.tokenTag(p.tok_i) == .string_literal) { while (p.eatToken(.string_literal)) |_| { switch (p.tokenTag(p.tok_i)) { .comma => p.tok_i += 1, @@ -2862,8 +2867,25 @@ fn expectAsmExpr(p: *Parse) !Node.Index { else => try p.warnExpected(.comma), } } + const rparen = try p.expectToken(.r_paren); + const span = try p.listToSpan(p.scratch.items[scratch_top..]); + return p.addNode(.{ + .tag = .asm_legacy, + .main_token = asm_token, + .data = .{ .node_and_extra = .{ + template, + try p.addExtra(Node.AsmLegacy{ + .items_start = span.start, + .items_end = span.end, + .rparen = rparen, + }), + } }, + }); } - } + + break :clobbers (try p.expectExpr()).toOptional(); + } else .none; + const rparen = try p.expectToken(.r_paren); const span = try p.listToSpan(p.scratch.items[scratch_top..]); return p.addNode(.{ @@ -2874,6 +2896,7 @@ fn expectAsmExpr(p: *Parse) !Node.Index { try p.addExtra(Node.Asm{ .items_start = span.start, .items_end = span.end, + .clobbers = clobbers, .rparen = rparen, }), } }, diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index c53ec630ba..1bb0b9d37f 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -1939,11 +1939,6 @@ pub const Inst = struct { /// `operand` is payload index to `BinNode`. builtin_extern, /// Inline assembly. - /// `small`: - /// * 0b00000000_000XXXXX - `outputs_len`. - /// * 0b000000XX_XXX00000 - `inputs_len`. - /// * 0b0XXXXX00_00000000 - `clobbers_len`. - /// * 0bX0000000_00000000 - is volatile /// `operand` is payload index to `Asm`. @"asm", /// Same as `asm` except the assembly template is not a string literal but a comptime @@ -2495,7 +2490,6 @@ pub const Inst = struct { /// Trailing: /// 0. Output for every outputs_len /// 1. Input for every inputs_len - /// 2. clobber: NullTerminatedString // index into string_bytes (null terminated) for every clobbers_len. pub const Asm = struct { src_node: Ast.Node.Offset, // null-terminated string index @@ -2505,6 +2499,13 @@ pub const Inst = struct { /// 0b1 - operand is a type; asm expression has the output as the result. /// 0b0X is the first output, 0bX0 is the second, etc. output_type_bits: u32, + clobbers: Ref, + + pub const Small = packed struct(u16) { + is_volatile: bool, + outputs_len: u7, + inputs_len: u8, + }; pub const Output = struct { /// index into string_bytes (null terminated) @@ -3482,6 +3483,7 @@ pub const Inst = struct { extern_options, type_info, branch_hint, + clobbers, // Values calling_convention_c, calling_convention_inline, diff --git a/lib/std/zig/ZonGen.zig b/lib/std/zig/ZonGen.zig index ccc09a82ce..44b59a328e 100644 --- a/lib/std/zig/ZonGen.zig +++ b/lib/std/zig/ZonGen.zig @@ -227,7 +227,7 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator => try zg.addErrorNode(node, "control flow is not allowed in ZON", .{}), .@"comptime" => try zg.addErrorNode(node, "keyword 'comptime' is not allowed in ZON", .{}), - .asm_simple, .@"asm" => try zg.addErrorNode(node, "inline asm is not allowed in ZON", .{}), + .asm_simple, .@"asm", .asm_legacy => try zg.addErrorNode(node, "inline asm is not allowed in ZON", .{}), .builtin_call_two, .builtin_call_two_comma, diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 56ab032771..f793adab90 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -852,6 +852,9 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { .@"asm", => return renderAsm(r, tree.fullAsm(node).?, space), + // To be removed after 0.15.0 is tagged + .asm_legacy => return renderAsmLegacy(r, tree.legacyAsm(node).?, space), + .enum_literal => { try renderToken(r, tree.nodeMainToken(node) - 1, .none); // . return renderIdentifier(r, tree.nodeMainToken(node), space, .eagerly_unquote); // name @@ -2363,9 +2366,9 @@ fn renderContainerDecl( return renderToken(r, rbrace, space); // rbrace } -fn renderAsm( +fn renderAsmLegacy( r: *Render, - asm_node: Ast.full.Asm, + asm_node: Ast.full.AsmLegacy, space: Space, ) Error!void { const tree = r.tree; @@ -2391,12 +2394,17 @@ fn renderAsm( try renderToken(r, first_clobber - 2, .none); try renderToken(r, first_clobber - 1, .space); + try ais.writer().writeAll(".{ "); + var tok_i = first_clobber; while (true) : (tok_i += 1) { - try renderToken(r, tok_i, .none); + try ais.writer().writeAll(".@"); + try ais.writer().writeAll(tokenSliceForRender(tree, tok_i)); + tok_i += 1; switch (tree.tokenTag(tok_i)) { .r_paren => { + try ais.writer().writeAll(" }"); ais.popIndent(); return renderToken(r, tok_i, space); }, @@ -2412,10 +2420,7 @@ fn renderAsm( } } } else { - // asm ("foo") - try renderExpression(r, asm_node.ast.template, .none); - ais.popIndent(); - return renderToken(r, asm_node.ast.rparen, space); // rparen + unreachable; } } @@ -2499,13 +2504,18 @@ fn renderAsm( }; try renderToken(r, colon3, .space); // : + try ais.writer().writeAll(".{ "); const first_clobber = asm_node.first_clobber.?; var tok_i = first_clobber; while (true) { switch (tree.tokenTag(tok_i + 1)) { .r_paren => { ais.setIndentDelta(indent_delta); - try renderToken(r, tok_i, .newline); + try ais.writer().writeAll(".@"); + const lexeme = tokenSliceForRender(tree, tok_i); + try ais.writer().writeAll(lexeme); + try ais.writer().writeAll(" }"); + try renderSpace(r, tok_i, lexeme.len, .newline); ais.popIndent(); return renderToken(r, tok_i + 1, space); }, @@ -2513,12 +2523,17 @@ fn renderAsm( switch (tree.tokenTag(tok_i + 2)) { .r_paren => { ais.setIndentDelta(indent_delta); - try renderToken(r, tok_i, .newline); + try ais.writer().writeAll(".@"); + const lexeme = tokenSliceForRender(tree, tok_i); + try ais.writer().writeAll(lexeme); + try ais.writer().writeAll(" }"); + try renderSpace(r, tok_i, lexeme.len, .newline); ais.popIndent(); return renderToken(r, tok_i + 2, space); }, else => { - try renderToken(r, tok_i, .none); + try ais.writer().writeAll(".@"); + try ais.writer().writeAll(tokenSliceForRender(tree, tok_i)); try renderToken(r, tok_i + 1, .space); tok_i += 2; }, @@ -2529,6 +2544,131 @@ fn renderAsm( } } +fn renderAsm( + r: *Render, + asm_node: Ast.full.Asm, + space: Space, +) Error!void { + const tree = r.tree; + const ais = r.ais; + + try renderToken(r, asm_node.ast.asm_token, .space); // asm + + if (asm_node.volatile_token) |volatile_token| { + try renderToken(r, volatile_token, .space); // volatile + try renderToken(r, volatile_token + 1, .none); // lparen + } else { + try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen + } + + if (asm_node.ast.items.len == 0) { + try ais.forcePushIndent(.normal); + if (asm_node.ast.clobbers.unwrap()) |clobbers| { + // asm ("foo" ::: clobbers) + try renderExpression(r, asm_node.ast.template, .space); + // Render the three colons. + const first_clobber = tree.firstToken(clobbers); + try renderToken(r, first_clobber - 3, .none); + try renderToken(r, first_clobber - 2, .none); + try renderToken(r, first_clobber - 1, .space); + try renderExpression(r, clobbers, .none); + ais.popIndent(); + return renderToken(r, asm_node.ast.rparen, space); // rparen + } + + // asm ("foo") + try renderExpression(r, asm_node.ast.template, .none); + ais.popIndent(); + return renderToken(r, asm_node.ast.rparen, space); // rparen + } + + try ais.forcePushIndent(.normal); + try renderExpression(r, asm_node.ast.template, .newline); + ais.setIndentDelta(asm_indent_delta); + const colon1 = tree.lastToken(asm_node.ast.template) + 1; + + const colon2 = if (asm_node.outputs.len == 0) colon2: { + try renderToken(r, colon1, .newline); // : + break :colon2 colon1 + 1; + } else colon2: { + try renderToken(r, colon1, .space); // : + + try ais.forcePushIndent(.normal); + for (asm_node.outputs, 0..) |asm_output, i| { + if (i + 1 < asm_node.outputs.len) { + const next_asm_output = asm_node.outputs[i + 1]; + try renderAsmOutput(r, asm_output, .none); + + const comma = tree.firstToken(next_asm_output) - 1; + try renderToken(r, comma, .newline); // , + try renderExtraNewlineToken(r, tree.firstToken(next_asm_output)); + } else if (asm_node.inputs.len == 0 and asm_node.ast.clobbers == .none) { + try ais.pushSpace(.comma); + try renderAsmOutput(r, asm_output, .comma); + ais.popSpace(); + ais.popIndent(); + ais.setIndentDelta(indent_delta); + ais.popIndent(); + return renderToken(r, asm_node.ast.rparen, space); // rparen + } else { + try ais.pushSpace(.comma); + try renderAsmOutput(r, asm_output, .comma); + ais.popSpace(); + const comma_or_colon = tree.lastToken(asm_output) + 1; + ais.popIndent(); + break :colon2 switch (tree.tokenTag(comma_or_colon)) { + .comma => comma_or_colon + 1, + else => comma_or_colon, + }; + } + } else unreachable; + }; + + const colon3 = if (asm_node.inputs.len == 0) colon3: { + try renderToken(r, colon2, .newline); // : + break :colon3 colon2 + 1; + } else colon3: { + try renderToken(r, colon2, .space); // : + try ais.forcePushIndent(.normal); + for (asm_node.inputs, 0..) |asm_input, i| { + if (i + 1 < asm_node.inputs.len) { + const next_asm_input = asm_node.inputs[i + 1]; + try renderAsmInput(r, asm_input, .none); + + const first_token = tree.firstToken(next_asm_input); + try renderToken(r, first_token - 1, .newline); // , + try renderExtraNewlineToken(r, first_token); + } else if (asm_node.ast.clobbers == .none) { + try ais.pushSpace(.comma); + try renderAsmInput(r, asm_input, .comma); + ais.popSpace(); + ais.popIndent(); + ais.setIndentDelta(indent_delta); + ais.popIndent(); + return renderToken(r, asm_node.ast.rparen, space); // rparen + } else { + try ais.pushSpace(.comma); + try renderAsmInput(r, asm_input, .comma); + ais.popSpace(); + const comma_or_colon = tree.lastToken(asm_input) + 1; + ais.popIndent(); + break :colon3 switch (tree.tokenTag(comma_or_colon)) { + .comma => comma_or_colon + 1, + else => comma_or_colon, + }; + } + } + unreachable; + }; + + try renderToken(r, colon3, .space); // : + const clobbers = asm_node.ast.clobbers.unwrap().?; + try renderExpression(r, clobbers, .none); + ais.setIndentDelta(indent_delta); + ais.popIndent(); + return renderToken(r, asm_node.ast.rparen, space); // rparen +} + fn renderCall( r: *Render, call: Ast.full.Call, diff --git a/src/Air.zig b/src/Air.zig index 0bd496ca2c..7708038638 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -1413,19 +1413,20 @@ pub const ShuffleTwoMask = enum(u32) { /// terminated string. /// - name: memory at this position is reinterpreted as a null /// terminated string. pad to the next u32 after the null byte. -/// 4. for every clobbers_len -/// - clobber_name: memory at this position is reinterpreted as a null -/// terminated string. pad to the next u32 after the null byte. -/// 5. A number of u32 elements follow according to the equation `(source_len + 3) / 4`. +/// 4. A number of u32 elements follow according to the equation `(source_len + 3) / 4`. /// Memory starting at this position is reinterpreted as the source bytes. pub const Asm = struct { /// Length of the assembly source in bytes. source_len: u32, - outputs_len: u32, inputs_len: u32, - /// The MSB is `is_volatile`. - /// The rest of the bits are `clobbers_len`. - flags: u32, + /// A comptime `std.builtin.assembly.Clobbers` value for the target architecture. + clobbers: InternPool.Index, + flags: Flags, + + pub const Flags = packed struct(u32) { + outputs_len: u31, + is_volatile: bool, + }; }; pub const Cmpxchg = struct { @@ -1749,7 +1750,7 @@ pub fn extraData(air: Air, comptime T: type, index: usize) struct { data: T, end @field(result, field.name) = switch (field.type) { u32 => air.extra.items[i], InternPool.Index, Inst.Ref => @enumFromInt(air.extra.items[i]), - i32, CondBr.BranchHints => @bitCast(air.extra.items[i]), + i32, CondBr.BranchHints, Asm.Flags => @bitCast(air.extra.items[i]), else => @compileError("bad field type: " ++ @typeName(field.type)), }; i += 1; diff --git a/src/Air/Liveness.zig b/src/Air/Liveness.zig index e369428636..7f5f99e8fe 100644 --- a/src/Air/Liveness.zig +++ b/src/Air/Liveness.zig @@ -1208,8 +1208,9 @@ fn analyzeInst( .assembly => { const extra = a.air.extraData(Air.Asm, inst_datas[@intFromEnum(inst)].ty_pl.payload); + const outputs_len = extra.data.flags.outputs_len; var extra_i: usize = extra.end; - const outputs = @as([]const Air.Inst.Ref, @ptrCast(a.air.extra.items[extra_i..][0..extra.data.outputs_len])); + const outputs = @as([]const Air.Inst.Ref, @ptrCast(a.air.extra.items[extra_i..][0..outputs_len])); extra_i += outputs.len; const inputs = @as([]const Air.Inst.Ref, @ptrCast(a.air.extra.items[extra_i..][0..extra.data.inputs_len])); extra_i += inputs.len; diff --git a/src/Air/Liveness/Verify.zig b/src/Air/Liveness/Verify.zig index b1e13dbf40..a1cce26a64 100644 --- a/src/Air/Liveness/Verify.zig +++ b/src/Air/Liveness/Verify.zig @@ -366,16 +366,11 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .assembly => { const ty_pl = data[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); + const outputs_len = extra.data.flags.outputs_len; var extra_i = extra.end; - const outputs = @as( - []const Air.Inst.Ref, - @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]), - ); + const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; - const inputs = @as( - []const Air.Inst.Ref, - @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]), - ); + const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; var bt = self.liveness.iterateBigTomb(inst); diff --git a/src/Air/print.zig b/src/Air/print.zig index 8ff199c02f..60e62097c3 100644 --- a/src/Air/print.zig +++ b/src/Air/print.zig @@ -1,5 +1,6 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const assert = std.debug.assert; const build_options = @import("build_options"); const Zcu = @import("../Zcu.zig"); @@ -9,7 +10,7 @@ const Air = @import("../Air.zig"); const InternPool = @import("../InternPool.zig"); pub fn write(air: Air, stream: *std.io.Writer, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { - comptime std.debug.assert(build_options.enable_debug_extensions); + comptime assert(build_options.enable_debug_extensions); const instruction_bytes = air.instructions.len * // Here we don't use @sizeOf(Air.Inst.Data) because it would include // the debug safety tag but we want to measure release size. @@ -59,7 +60,7 @@ pub fn writeInst( pt: Zcu.PerThread, liveness: ?Air.Liveness, ) void { - comptime std.debug.assert(build_options.enable_debug_extensions); + comptime assert(build_options.enable_debug_extensions); var writer: Writer = .{ .pt = pt, .gpa = pt.zcu.gpa, @@ -643,8 +644,8 @@ const Writer = struct { fn writeAssembly(w: *Writer, s: *std.io.Writer, inst: Air.Inst.Index) Error!void { const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = w.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len = @as(u31, @truncate(extra.data.flags)); + const is_volatile = extra.data.flags.is_volatile; + const outputs_len = extra.data.flags.outputs_len; var extra_i: usize = extra.end; var op_index: usize = 0; @@ -655,7 +656,7 @@ const Writer = struct { try s.writeAll(", volatile"); } - const outputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.outputs_len])); + const outputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..outputs_len])); extra_i += outputs.len; const inputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.inputs_len])); extra_i += inputs.len; @@ -695,19 +696,35 @@ const Writer = struct { try s.writeByte(')'); } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); - const clobber = std.mem.sliceTo(extra_bytes, 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - try s.writeAll(", ~{"); - try s.writeAll(clobber); - try s.writeAll("}"); - } + const zcu = w.pt.zcu; + const ip = &zcu.intern_pool; + const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const struct_type: Type = .fromInterned(aggregate.ty); + switch (aggregate.storage) { + .elems => |elems| for (elems, 0..) |elem, i| { + switch (elem) { + .bool_true => { + const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?; + assert(clobber.len != 0); + try s.writeAll(", ~{"); + try s.writeAll(clobber); + try s.writeAll("}"); + }, + .bool_false => continue, + else => unreachable, + } + }, + .repeated_elem => |elem| { + try s.writeAll(", "); + try s.writeAll(switch (elem) { + .bool_true => "<all clobbers>", + .bool_false => "<no clobbers>", + else => unreachable, + }); + }, + .bytes => |bytes| { + try s.print(", {x}", .{bytes}); + }, } const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len]; try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)}); diff --git a/src/Air/types_resolved.zig b/src/Air/types_resolved.zig index 2bf1ef108f..44669b82df 100644 --- a/src/Air/types_resolved.zig +++ b/src/Air/types_resolved.zig @@ -416,8 +416,9 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { if (!checkType(data.ty_pl.ty.toType(), zcu)) return false; // Luckily, we only care about the inputs and outputs, so we don't have to do // the whole null-terminated string dance. - const outputs: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..extra.data.outputs_len]); - const inputs: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end + extra.data.outputs_len ..][0..extra.data.inputs_len]); + const outputs_len = extra.data.flags.outputs_len; + const outputs: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end..][0..outputs_len]); + const inputs: []const Air.Inst.Ref = @ptrCast(air.extra.items[extra.end + outputs_len ..][0..extra.data.inputs_len]); for (outputs) |output| if (output != .none and !checkRef(output, zcu)) return false; for (inputs) |input| if (input != .none and !checkRef(input, zcu)) return false; }, diff --git a/src/InternPool.zig b/src/InternPool.zig index ba5725ec30..c9036da45b 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -54,6 +54,7 @@ namespace_name_deps: std.AutoArrayHashMapUnmanaged(NamespaceNameKey, DepEntry.In memoized_state_main_deps: DepEntry.Index.Optional, memoized_state_panic_deps: DepEntry.Index.Optional, memoized_state_va_list_deps: DepEntry.Index.Optional, +memoized_state_assembly_deps: DepEntry.Index.Optional, /// Given a `Depender`, points to an entry in `dep_entries` whose `depender` /// matches. The `next_dependee` field can be used to iterate all such entries @@ -96,6 +97,7 @@ pub const empty: InternPool = .{ .memoized_state_main_deps = .none, .memoized_state_panic_deps = .none, .memoized_state_va_list_deps = .none, + .memoized_state_assembly_deps = .none, .first_dependency = .empty, .dep_entries = .empty, .free_dep_entries = .empty, @@ -458,6 +460,8 @@ pub const MemoizedStateStage = enum(u32) { panic, /// Specifically `std.builtin.VaList`. See `Zcu.BuiltinDecl.stage`. va_list, + /// Everything within `std.builtin.assembly`. See `Zcu.BuiltinDecl.stage`. + assembly, }; pub const ComptimeUnit = extern struct { @@ -880,6 +884,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI .main => ip.memoized_state_main_deps.unwrap(), .panic => ip.memoized_state_panic_deps.unwrap(), .va_list => ip.memoized_state_va_list_deps.unwrap(), + .assembly => ip.memoized_state_assembly_deps.unwrap(), }, } orelse return .{ .ip = ip, @@ -915,6 +920,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend .main => &ip.memoized_state_main_deps, .panic => &ip.memoized_state_panic_deps, .va_list => &ip.memoized_state_va_list_deps, + .assembly => &ip.memoized_state_assembly_deps, }; if (deps.unwrap()) |first| { diff --git a/src/Sema.zig b/src/Sema.zig index 6672ba5b36..40ac0f958c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16413,10 +16413,10 @@ fn zirAsm( const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); const src = block.nodeOffset(extra.data.src_node); const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node }); - const outputs_len: u4 = @truncate(extended.small); - const inputs_len: u5 = @truncate(extended.small >> 4); - const clobbers_len: u6 = @truncate(extended.small >> 9); - const is_volatile = @as(u1, @truncate(extended.small >> 15)) != 0; + const small: Zir.Inst.Asm.Small = @bitCast(extended.small); + const outputs_len = small.outputs_len; + const inputs_len = small.inputs_len; + const is_volatile = small.is_volatile; const is_global_assembly = sema.func_index == .none; const zir_tags = sema.code.instructions.items(.tag); @@ -16432,7 +16432,7 @@ fn zirAsm( if (inputs_len != 0) { return sema.fail(block, src, "module-level assembly does not support inputs", .{}); } - if (clobbers_len != 0) { + if (extra.data.clobbers != .none) { return sema.fail(block, src, "module-level assembly does not support clobbers", .{}); } if (is_volatile) { @@ -16506,15 +16506,11 @@ fn zirAsm( inputs[arg_i] = .{ .c = constraint, .n = name }; } - const clobbers = try sema.arena.alloc([]const u8, clobbers_len); - for (clobbers) |*name| { - const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_i]); - name.* = sema.code.nullTerminatedString(name_index); - extra_i += 1; - - needed_capacity += name.*.len / 4 + 1; - } - + const clobbers = if (extra.data.clobbers == .none) empty: { + const clobbers_ty = try sema.getBuiltinType(src, .@"assembly.Clobbers"); + break :empty try sema.structInitEmpty(block, clobbers_ty, src, src); + } else try sema.resolveInst(extra.data.clobbers); // Already coerced by AstGen. + const clobbers_val = try sema.resolveConstDefinedValue(block, src, clobbers, .{ .simple = .clobber }); needed_capacity += (asm_source.len + 3) / 4; const gpa = sema.gpa; @@ -16525,9 +16521,12 @@ fn zirAsm( .ty = expr_ty, .payload = sema.addExtraAssumeCapacity(Air.Asm{ .source_len = @intCast(asm_source.len), - .outputs_len = outputs_len, .inputs_len = @intCast(args.len), - .flags = (@as(u32, @intFromBool(is_volatile)) << 31) | @as(u32, @intCast(clobbers.len)), + .clobbers = clobbers_val.toIntern(), + .flags = .{ + .is_volatile = is_volatile, + .outputs_len = outputs_len, + }, }), } }, }); @@ -16549,12 +16548,6 @@ fn zirAsm( buffer[input.c.len + 1 + input.n.len] = 0; sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; } - for (clobbers) |clobber| { - const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); - @memcpy(buffer[0..clobber.len], clobber); - buffer[clobber.len] = 0; - sema.air_extra.items.len += clobber.len / 4 + 1; - } { const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); @memcpy(buffer[0..asm_source.len], asm_source); @@ -26197,6 +26190,7 @@ fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD .extern_options => try sema.getBuiltinType(src, .ExternOptions), .type_info => try sema.getBuiltinType(src, .Type), .branch_hint => try sema.getBuiltinType(src, .BranchHint), + .clobbers => try sema.getBuiltinType(src, .@"assembly.Clobbers"), // zig fmt: on // Values are handled here. @@ -36546,7 +36540,7 @@ fn payloadToExtraItems(data: anytype) [@typeInfo(@TypeOf(data)).@"struct".fields inline for (&result, fields) |*val, field| { val.* = switch (field.type) { u32 => @field(data, field.name), - i32, Air.CondBr.BranchHints => @bitCast(@field(data, field.name)), + i32, Air.CondBr.BranchHints, Air.Asm.Flags => @bitCast(@field(data, field.name)), Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(data, field.name)), else => @compileError("bad field type: " ++ @typeName(field.type)), }; diff --git a/src/Zcu.zig b/src/Zcu.zig index b2b0f71e32..897bb6e89e 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -460,6 +460,9 @@ pub const BuiltinDecl = enum { VaList, + assembly, + @"assembly.Clobbers", + /// Determines what kind of validation will be done to the decl's value. pub fn kind(decl: BuiltinDecl) enum { type, func, string } { return switch (decl) { @@ -480,6 +483,8 @@ pub const BuiltinDecl = enum { .ExportOptions, .ExternOptions, .BranchHint, + .assembly, + .@"assembly.Clobbers", => .type, .Type, @@ -540,6 +545,7 @@ pub const BuiltinDecl = enum { /// Resolution of these values is done in three distinct stages: /// * Resolution of `std.builtin.Panic` and everything under it /// * Resolution of `VaList` + /// * Resolution of `assembly` /// * Everything else /// /// Panics are separated because they are provided by the user, so must be able to use @@ -548,14 +554,20 @@ pub const BuiltinDecl = enum { /// `VaList` is separate because its value depends on the target, so it needs some reflection /// machinery to work; additionally, it is `@compileError` on some targets, so must be referenced /// by itself. + /// + /// `assembly` is separate because its value depends on the target. pub fn stage(decl: BuiltinDecl) InternPool.MemoizedStateStage { - if (decl == .VaList) return .va_list; - - if (@intFromEnum(decl) <= @intFromEnum(BuiltinDecl.@"Type.Declaration")) { - return .main; - } else { - return .panic; - } + return switch (decl) { + .VaList => .va_list, + .assembly, .@"assembly.Clobbers" => .assembly, + else => { + if (@intFromEnum(decl) <= @intFromEnum(BuiltinDecl.@"Type.Declaration")) { + return .main; + } else { + return .panic; + } + }, + }; } /// Based on the tag name, determines how to access this decl; either as a direct child of the diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 1be84d964f..dc2308add1 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -635,6 +635,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized .main => .Type, .panic => .panic, .va_list => .VaList, + .assembly => .assembly, }; if (zcu.builtin_decl_values.get(to_check) != .none) return; } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 49baf7acad..0753cc5d16 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -6047,10 +6047,10 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void { fn airAsm(func: *Func, inst: Air.Inst.Index) !void { const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.Asm, ty_pl.payload); - const clobbers_len: u31 = @truncate(extra.data.flags); + const outputs_len = extra.data.flags.outputs_len; var extra_i: usize = extra.end; const outputs: []const Air.Inst.Ref = - @ptrCast(func.air.extra.items[extra_i..][0..extra.data.outputs_len]); + @ptrCast(func.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(func.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; @@ -6161,21 +6161,33 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void { args.appendAssumeCapacity(arg_mcv); } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(func.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory")) { - // nothing really to do - } else { - try func.register_manager.getReg(parseRegName(clobber) orelse - return func.fail("invalid clobber: '{s}'", .{clobber}), null); + const zcu = func.pt.zcu; + const ip = &zcu.intern_pool; + const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const struct_type: Type = .fromInterned(aggregate.ty); + switch (aggregate.storage) { + .elems => |elems| for (elems, 0..) |elem, i| { + switch (elem) { + .bool_true => { + const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?; + assert(clobber.len != 0); + if (std.mem.eql(u8, clobber, "memory")) { + // nothing really to do + } else { + try func.register_manager.getReg(parseRegName(clobber) orelse + return func.fail("invalid clobber: '{s}'", .{clobber}), null); + } + }, + .bool_false => continue, + else => unreachable, } - } + }, + .repeated_elem => |elem| switch (elem) { + .bool_true => @panic("TODO"), + .bool_false => {}, + else => unreachable, + }, + .bytes => @panic("TODO"), } const Label = struct { diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 9174a5850e..31a7f39d69 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -873,10 +873,10 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = (extra.data.flags & 0x80000000) != 0; - const clobbers_len: u31 = @truncate(extra.data.flags); + const is_volatile = extra.data.flags.is_volatile; + const outputs_len = extra.data.flags.outputs_len; var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i .. extra_i + extra.data.outputs_len]); + const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i .. extra_i + outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i .. extra_i + extra.data.inputs_len]); extra_i += inputs.len; @@ -921,17 +921,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { try self.genSetReg(self.typeOf(input), reg, arg_mcv); } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - // TODO honor these - } - } + // TODO honor the clobbers + _ = extra.data.clobbers; const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 80542b551f..6341f7e3d2 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -179788,9 +179788,9 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { const zcu = pt.zcu; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const clobbers_len: u31 = @truncate(extra.data.flags); + const outputs_len = extra.data.flags.outputs_len; var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]); + const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; @@ -179981,30 +179981,42 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { args.appendAssumeCapacity(arg_mcv); } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory") or - std.mem.eql(u8, clobber, "fpsr") or std.mem.eql(u8, clobber, "fpcr") or - std.mem.eql(u8, clobber, "mxcsr") or std.mem.eql(u8, clobber, "dirflag")) - { - // ok, sure - } else if (std.mem.eql(u8, clobber, "cc") or - std.mem.eql(u8, clobber, "flags") or - std.mem.eql(u8, clobber, "eflags") or - std.mem.eql(u8, clobber, "rflags")) - { - try self.spillEflagsIfOccupied(); - } else { - try self.register_manager.getReg(parseRegName(clobber) orelse - return self.fail("invalid clobber: '{s}'", .{clobber}), null); - } - } + const ip = &zcu.intern_pool; + const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const struct_type: Type = .fromInterned(aggregate.ty); + switch (aggregate.storage) { + .elems => |elems| for (elems, 0..) |elem, i| switch (elem) { + .bool_true => { + const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?; + assert(clobber.len != 0); + + if (std.mem.eql(u8, clobber, "memory") or + std.mem.eql(u8, clobber, "fpsr") or + std.mem.eql(u8, clobber, "fpcr") or + std.mem.eql(u8, clobber, "mxcsr") or + std.mem.eql(u8, clobber, "dirflag")) + { + // ok, sure + } else if (std.mem.eql(u8, clobber, "cc") or + std.mem.eql(u8, clobber, "flags") or + std.mem.eql(u8, clobber, "eflags") or + std.mem.eql(u8, clobber, "rflags")) + { + try self.spillEflagsIfOccupied(); + } else { + try self.register_manager.getReg(parseRegName(clobber) orelse + return self.fail("invalid clobber: '{s}'", .{clobber}), null); + } + }, + .bool_false => continue, + else => unreachable, + }, + .repeated_elem => |elem| switch (elem) { + .bool_true => @panic("TODO"), + .bool_false => {}, + else => unreachable, + }, + .bytes => @panic("TODO"), } const Label = struct { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 61bd5259ae..955a83bc33 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -5545,11 +5545,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const zcu = pt.zcu; const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = f.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len: u31 = @truncate(extra.data.flags); + const is_volatile = extra.data.flags.is_volatile; + const outputs_len = extra.data.flags.outputs_len; const gpa = f.object.dg.gpa; var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..extra.data.outputs_len]); + const outputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; @@ -5645,12 +5645,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.newline(); } } - for (0..clobbers_len) |_| { - const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - } { const asm_source = mem.sliceAsBytes(f.air.extra.items[extra_i..])[0..extra.data.source_len]; @@ -5757,17 +5751,28 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try w.writeByte(')'); } try w.writeByte(':'); - for (0..clobbers_len) |clobber_i| { - const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - if (clobber.len == 0) continue; - - if (clobber_i > 0) try w.writeByte(','); - try w.print(" {f}", .{fmtStringLiteral(clobber, null)}); + const ip = &zcu.intern_pool; + const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const struct_type: Type = .fromInterned(aggregate.ty); + switch (aggregate.storage) { + .elems => |elems| for (elems, 0..) |elem, i| switch (elem) { + .bool_true => { + const name = struct_type.structFieldName(i, zcu).toSlice(ip).?; + assert(name.len != 0); + try w.print(" {f}", .{fmtStringLiteral(name, null)}); + (try w.writableArray(1))[0] = ','; + }, + .bool_false => continue, + else => unreachable, + }, + .repeated_elem => |elem| switch (elem) { + .bool_true => @panic("TODO"), + .bool_false => {}, + else => unreachable, + }, + .bytes => @panic("TODO"), } + w.undo(1); // erase the last comma try w.writeAll(");"); try f.object.newline(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e726a2bdd3..51e861cf6e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -7241,19 +7241,20 @@ pub const FuncGen = struct { const o = self.ng.object; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len: u31 = @truncate(extra.data.flags); + const is_volatile = extra.data.flags.is_volatile; + const outputs_len = extra.data.flags.outputs_len; + const gpa = self.gpa; var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]); + const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; var llvm_constraints: std.ArrayListUnmanaged(u8) = .empty; - defer llvm_constraints.deinit(self.gpa); + defer llvm_constraints.deinit(gpa); - var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -7290,7 +7291,7 @@ pub const FuncGen = struct { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 3); + try llvm_constraints.ensureUnusedCapacity(gpa, constraint.len + 3); if (total_i != 0) { llvm_constraints.appendAssumeCapacity(','); } @@ -7399,7 +7400,7 @@ pub const FuncGen = struct { } } - try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); + try llvm_constraints.ensureUnusedCapacity(gpa, constraint.len + 1); if (total_i != 0) { llvm_constraints.appendAssumeCapacity(','); } @@ -7456,7 +7457,7 @@ pub const FuncGen = struct { llvm_param_types[llvm_param_i] = llvm_elem_ty; } - try llvm_constraints.print(self.gpa, ",{d}", .{output_index}); + try llvm_constraints.print(gpa, ",{d}", .{output_index}); // In the case of indirect inputs, LLVM requires the callsite to have // an elementtype(<ty>) attribute. @@ -7466,24 +7467,41 @@ pub const FuncGen = struct { total_i += 1; } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); - if (total_i != 0) { - llvm_constraints.appendAssumeCapacity(','); + const ip = &zcu.intern_pool; + const aggregate = ip.indexToKey(extra.data.clobbers).aggregate; + const struct_type: Type = .fromInterned(aggregate.ty); + switch (aggregate.storage) { + .elems => |elems| for (elems, 0..) |elem, i| { + switch (elem) { + .bool_true => { + const name = struct_type.structFieldName(i, zcu).toSlice(ip).?; + try llvm_constraints.ensureUnusedCapacity(gpa, name.len + 4); + if (total_i != 0) llvm_constraints.appendAssumeCapacity(','); + llvm_constraints.appendSliceAssumeCapacity("~{"); + llvm_constraints.appendSliceAssumeCapacity(name); + llvm_constraints.appendSliceAssumeCapacity("}"); + + total_i += 1; + }, + .bool_false => continue, + else => unreachable, } - llvm_constraints.appendSliceAssumeCapacity("~{"); - llvm_constraints.appendSliceAssumeCapacity(clobber); - llvm_constraints.appendSliceAssumeCapacity("}"); + }, + .repeated_elem => |elem| switch (elem) { + .bool_true => for (0..struct_type.structFieldCount(zcu)) |i| { + const name = struct_type.structFieldName(i, zcu).toSlice(ip).?; + try llvm_constraints.ensureUnusedCapacity(gpa, name.len + 4); + if (total_i != 0) llvm_constraints.appendAssumeCapacity(','); + llvm_constraints.appendSliceAssumeCapacity("~{"); + llvm_constraints.appendSliceAssumeCapacity(name); + llvm_constraints.appendSliceAssumeCapacity("}"); - total_i += 1; - } + total_i += 1; + }, + .bool_false => {}, + else => unreachable, + }, + .bytes => @panic("TODO"), } // We have finished scanning through all inputs/outputs, so the number of @@ -7497,13 +7515,13 @@ pub const FuncGen = struct { // to be buggy and regress often. switch (target.cpu.arch) { .x86_64, .x86 => { - if (total_i != 0) try llvm_constraints.append(self.gpa, ','); - try llvm_constraints.appendSlice(self.gpa, "~{dirflag},~{fpsr},~{flags}"); + if (total_i != 0) try llvm_constraints.append(gpa, ','); + try llvm_constraints.appendSlice(gpa, "~{dirflag},~{fpsr},~{flags}"); total_i += 3; }, .mips, .mipsel, .mips64, .mips64el => { - if (total_i != 0) try llvm_constraints.append(self.gpa, ','); - try llvm_constraints.appendSlice(self.gpa, "~{$1}"); + if (total_i != 0) try llvm_constraints.append(gpa, ','); + try llvm_constraints.appendSlice(gpa, "~{$1}"); total_i += 1; }, else => {}, @@ -7512,7 +7530,7 @@ pub const FuncGen = struct { const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; // hackety hacks until stage2 has proper inline asm in the frontend. - var rendered_template = std.ArrayList(u8).init(self.gpa); + var rendered_template = std.ArrayList(u8).init(gpa); defer rendered_template.deinit(); const State = enum { start, percent, input, modifier }; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 255e957c8b..292f5a62fc 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -6387,13 +6387,13 @@ const NavGen = struct { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len: u31 = @truncate(extra.data.flags); + const is_volatile = extra.data.flags.is_volatile; + const outputs_len = extra.data.flags.outputs_len; if (!is_volatile and self.liveness.isUnused(inst)) return null; var extra_i: usize = extra.end; - const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.outputs_len]); + const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..outputs_len]); extra_i += outputs.len; const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra.items[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; @@ -6402,7 +6402,7 @@ const NavGen = struct { return self.todo("implement inline asm with more than 1 output", .{}); } - var as = SpvAssembler{ + var as: SpvAssembler = .{ .gpa = self.gpa, .spv = self.spv, .func = &self.func, @@ -6486,14 +6486,8 @@ const NavGen = struct { } } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra.items[extra_i..]), 0); - extra_i += clobber.len / 4 + 1; - // TODO: Record clobber and use it somewhere. - } - } + // TODO: do something with clobbers + _ = extra.data.clobbers; const asm_source = std.mem.sliceAsBytes(self.air.extra.items[extra_i..])[0..extra.data.source_len]; |
