diff --git a/src/Sema.zig b/src/Sema.zig index cceb34cc6d0c73f1ccea3704c80ec81846619185..1876ec5dbea763a21673adc8ed0ad7f51d07b703 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -15593,18 +15593,35 @@ fn zirAsm( const name = sema.code.nullTerminatedString(output.data.name); - if (is_type) { - // Indicate the output is the asm instruction return value. - arg.* = .none; - const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); - expr_ty = Air.internedToRef(out_ty.toIntern()); - } else { - const inst = sema.resolveInst(output.data.operand); - if (!sema.checkRuntimeValue(inst)) { - const output_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); - return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?)); + const out_ty: Type = out_ty: { + if (is_type) { + // Indicate the output is the asm instruction return value. + arg.* = .none; + + const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); + try sema.ensureLayoutResolved(out_ty, ret_ty_src, .asm_out_type); + expr_ty = .fromType(out_ty); + break :out_ty out_ty; + } else { + const inst = sema.resolveInst(output.data.operand); + arg.* = inst; + + if (!sema.checkRuntimeValue(inst)) { + const output_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); + return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?)); + } + break :out_ty sema.typeOf(inst).childType(zcu); } - arg.* = inst; + }; + if (!out_ty.hasWellDefinedLayout(zcu)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(output_src, "invalid inline assembly output type; '{f}' does not have a guaranteed in-memory layout", .{ + out_ty.fmt(pt), + }); + errdefer msg.destroy(gpa); + try sema.addDeclaredHereNote(msg, out_ty); + break :msg msg; + }); } const constraint = sema.code.nullTerminatedString(output.data.constraint); diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig index 39e20f8a5e6375ab79d42a5ea457ac0ab0ae08dc..00da75abecbbc211ebdda06bac46611fb7c87193 100644 --- a/src/Sema/type_resolution.zig +++ b/src/Sema/type_resolution.zig @@ -34,6 +34,7 @@ pub const LayoutResolveReason = enum { bit_ptr_child, @"export", @"extern", + asm_out_type, builtin_type, /// Written after string: "while resolving type 'T' " @@ -60,6 +61,7 @@ pub const LayoutResolveReason = enum { .bit_ptr_child => "for bit size check here", .@"export" => "for export here", .@"extern" => "for extern declaration here", + .asm_out_type => "for inline assembly output type declared here", .builtin_type => "from 'std.builtin'", // zig fmt: on }; diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index ebfd5bb235abea6b64ee6e19fba0554b756addf3..707b9eb70eddbc1fab4c333b2a8c39376f018e5a 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -175,3 +175,74 @@ test "asm modifiers (AArch64)" { ); try expectEqual(2 * x, double); } + +test "packed output types (x86_64)" { + if (builtin.target.cpu.arch != .x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly + + const S = packed struct(u32) { x: u32 }; + { + const s: S = asm volatile ("mov $123, %[ret]" + : [ret] "=r" (-> S), + ); + try expect(s.x == 123); + } + { + var s: S = undefined; + asm volatile ("mov $123, %[ret]" + : [ret] "=r" (s), + ); + try expect(s.x == 123); + } + + const U = packed union(u32) { x: u32 }; + { + const u: U = asm volatile ("mov $123, %[ret]" + : [ret] "=r" (-> U), + ); + try expect(u.x == 123); + } + { + var u: U = undefined; + asm volatile ("mov $123, %[ret]" + : [ret] "=r" (u), + ); + try expect(u.x == 123); + } +} + +test "extern output types (x86_64)" { + if (builtin.target.cpu.arch != .x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/31531 + + const S = extern struct { x: u32 }; + { + const s: S = asm volatile ("mov $123, %[ret]" + : [ret] "=r" (-> S), + ); + try expect(s.x == 123); + } + { + var s: S = undefined; + asm volatile ("mov $123, %[ret]" + : [ret] "=r" (s), + ); + try expect(s.x == 123); + } + + const U = extern union { x: u32 }; + { + const u: U = asm volatile ("mov $123, %[ret]" + : [ret] "=r" (-> U), + ); + try expect(u.x == 123); + } + { + var u: U = undefined; + asm volatile ("mov $123, %[ret]" + : [ret] "=r" (u), + ); + try expect(u.x == 123); + } +} diff --git a/test/cases/compile_errors/asm_output_type_no_guaranteed_in_memory_layout.zig b/test/cases/compile_errors/asm_output_type_no_guaranteed_in_memory_layout.zig new file mode 100644 index 0000000000000000000000000000000000000000..85be7d3c27ba384a96eb7575bdb579e4a3e3bdff --- /dev/null +++ b/test/cases/compile_errors/asm_output_type_no_guaranteed_in_memory_layout.zig @@ -0,0 +1,38 @@ +const S = struct { x: u32 }; +export fn entry1() void { + const s = asm volatile ("" + : [_] "=r" (-> S), + ); + _ = s; +} +export fn entry2() void { + var s: S = undefined; + asm volatile ("" + : [_] "=r" (s), + ); +} + +const U = union { x: u32 }; +export fn entry3() void { + const u = asm volatile ("" + : [_] "=r" (-> U), + ); + _ = u; +} +export fn entry4() void { + var u: U = undefined; + asm volatile ("" + : [_] "=r" (u), + ); +} + +// error +// +// :4:24: error: invalid inline assembly output type; 'tmp.S' does not have a guaranteed in-memory layout +// :1:11: note: struct declared here +// :11:21: error: invalid inline assembly output type; 'tmp.S' does not have a guaranteed in-memory layout +// :1:11: note: struct declared here +// :18:24: error: invalid inline assembly output type; 'tmp.U' does not have a guaranteed in-memory layout +// :15:11: note: union declared here +// :25:21: error: invalid inline assembly output type; 'tmp.U' does not have a guaranteed in-memory layout +// :15:11: note: union declared here