From 0bb0b0d4cb0472bb702d52b6437a8f6d6688570b Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Thu, 15 Jun 2023 07:19:56 -0400 Subject: [PATCH] Sema: catch invalid asm input operands Fixes #7843 --- src/Sema.zig | 36 ++++++++++ src/Zcu.zig | 18 +++++ test/behavior/asm.zig | 16 ++++- .../compile_errors/bad_asm_input_operands.zig | 70 +++++++++++++++++++ 4 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 test/cases/compile_errors/bad_asm_input_operands.zig diff --git a/src/Sema.zig b/src/Sema.zig index 7ab60adbf244..49200a04955d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17629,6 +17629,42 @@ fn zirAsm( .comptime_int => arg.* = try sema.coerce(block, Type.usize, uncasted_arg, src), .comptime_float => arg.* = try sema.coerce(block, Type.f64, uncasted_arg, src), else => { + const input_op_src = block.src(.{ + .asm_input_op = .{ + .asm_node_offset = extra.data.src_node, + .input_index = @intCast(arg_i), + }, + }); + if (try uncasted_arg_ty.comptimeOnlySema(pt)) { + const target = pt.zcu.getTarget(); + const arch = target.cpu.arch; + const is_spirv = arch.isSpirV(); + // SPIR-V is a typed language so we want to be able to pass types through. + if (uncasted_arg_ty.zigTypeTag(zcu) != .type or !is_spirv) { + return sema.fail( + block, + input_op_src, + "type '{}' is comptime-only and cannot be used for an assembly input operand", + .{uncasted_arg_ty.fmt(pt)}, + ); + } + } + if (!try uncasted_arg_ty.hasRuntimeBitsSema(pt)) { + return sema.fail( + block, + input_op_src, + "type '{}' does not have runtime bits and cannot be used for an assembly input operand", + .{uncasted_arg_ty.fmt(pt)}, + ); + } + if (!uncasted_arg_ty.hasWellDefinedLayout(zcu)) { + return sema.fail( + block, + input_op_src, + "type '{}' does not have a well-defined memory layout and cannot be used for an assembly input operand", + .{uncasted_arg_ty.fmt(pt)}, + ); + } arg.* = uncasted_arg; }, } diff --git a/src/Zcu.zig b/src/Zcu.zig index 148570d85a8d..39d5ec4e925c 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -989,6 +989,14 @@ pub const SrcLoc = struct { const node_datas = tree.nodes.items(.data); return tree.nodeToSpan(node_datas[asm_output].lhs); }, + .asm_input_op => |asm_input_op| { + const tree = try src_loc.file_scope.getTree(gpa); + const node = src_loc.relativeToNodeIndex(asm_input_op.asm_node_offset); + const full = tree.fullAsm(node).?; + const asm_input = full.inputs[asm_input_op.input_index]; + const node_datas = tree.nodes.items(.data); + return tree.nodeToSpan(node_datas[asm_input].lhs); + }, .node_offset_if_cond => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); @@ -1795,6 +1803,16 @@ pub const LazySrcLoc = struct { /// base node, which points to inline assembly AST node. Next, navigate /// to the return type expression. node_offset_asm_ret_ty: i32, + /// The source location points to an input operand of an inline assembly + /// expression, found by taking this AST node index offset from the containing + /// Decl AST node, which points to inline assembly AST node. Next, navigate + /// to the input operand expression. + asm_input_op: struct { + /// Points to the asm AST node. + asm_node_offset: i32, + /// Picks one of the inputs from the asm. + input_index: u32, + }, /// The source location points to the condition expression of an if /// expression, found by taking this AST node index offset from the containing /// base node, which points to an if expression AST node. Next, navigate diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index e82242f4257b..4ff383f3dd9f 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -150,12 +150,24 @@ test "struct/array/union types as input values" { ); // fails asm volatile ("" : - : [_] "m" (@as(struct { x: u32, y: u8 }, undefined)), + : [_] "m" (@as(packed struct { x: u32, y: u8 }, undefined)), ); // fails + // TODO: CBE struggles with unions here + if (builtin.zig_backend != .stage2_c) + asm volatile ("" + : + : [_] "m" (@as(packed union { x: u32, y: u8 }, undefined)), + ); // fails asm volatile ("" : - : [_] "m" (@as(union { x: u32, y: u8 }, undefined)), + : [_] "m" (@as(extern struct { x: u32, y: u8 }, undefined)), ); // fails + // TODO: CBE struggles with unions here + if (builtin.zig_backend != .stage2_c) + asm volatile ("" + : + : [_] "m" (@as(extern union { x: u32, y: u8 }, undefined)), + ); // fails } extern fn this_is_my_alias() i32; diff --git a/test/cases/compile_errors/bad_asm_input_operands.zig b/test/cases/compile_errors/bad_asm_input_operands.zig new file mode 100644 index 000000000000..2685ef8f1742 --- /dev/null +++ b/test/cases/compile_errors/bad_asm_input_operands.zig @@ -0,0 +1,70 @@ +export fn a() void { + asm volatile ("" + : + : [_] "{al}" (u8), + ); +} +export fn b() void { + asm volatile ("" + : + : [_] "{a}" (0), + [_] "{x}" (&&void), + ); +} +export fn c() void { + const A = struct {}; + asm volatile ("" + : + : [_] "{x}" (1), + [_] "{x}" (A{}), + ); +} +export fn d() void { + const A = struct { x: u8 }; + asm volatile ("" + : + : [_] "{x}" (((packed struct { x: u8 }){ .x = 1 })), + [_] "{x}" (extern struct { x: u8 }{ .x = 1 }), + [_] "{x}" (A{ .x = 1 }), + ); +} +export fn e() void { + asm volatile ("" + : + : [_] "{x}" (@Vector(3, u8){ 1, 2, 3 }), + [_] "{x}" ([2]*const type{ &u8, &u8 }), + ); +} +export fn f() void { + asm volatile ("" + : + : [_] "{x}" (undefined), + ); +} +export fn g() void { + asm volatile ("" + : + : [_] "{x}" ({}), + ); +} +export fn h() void { + asm volatile ("" + : + : [_] "{x}" (@as([]const u8, "hello")), + ); +} + +// error +// backend=stage2 +// target=native +// +// :4:23: error: type 'type' is comptime-only and cannot be used for an assembly input operand +// :11:22: error: type '*const *const type' is comptime-only and cannot be used for an assembly input operand +// :19:23: error: type 'tmp.c.A' does not have runtime bits and cannot be used for an assembly input operand +// :15:15: note: struct declared here +// :28:23: error: type 'tmp.d.A' does not have a well-defined memory layout and cannot be used for an assembly input operand +// :23:15: note: struct declared here +// :35:36: error: type '[2]*const type' is comptime-only and cannot be used for an assembly input operand +// :41:22: error: type '@TypeOf(undefined)' is comptime-only and cannot be used for an assembly input operand +// :47:22: error: type 'void' does not have runtime bits and cannot be used for an assembly input operand +// :53:22: error: type '[]const u8' does not have a well-defined memory layout and cannot be used for an assembly input operand