| author | |
| committer | |
| log | 28c5cc390c909db06782eb2366d315c5da501a5f |
| tree | a3edbd02ec35d150cb6848c7783843182265a17e |
| parent | dbb4c8d1514f351bbef4a6977a0b188a3c6b81dc |
6 files changed, 100 insertions(+), 45 deletions(-)
lib/std/zig/AstGen.zig+1-1| ... | ... | @@ -13000,9 +13000,9 @@ const GenZir = struct { |
| 13000 | 13000 | } |
| 13001 | 13001 | |
| 13002 | 13002 | const small: Zir.Inst.Asm.Small = .{ |
| 13003 | .is_volatile = args.is_volatile, | |
| 13003 | 13004 | .outputs_len = @intCast(args.outputs.len), |
| 13004 | 13005 | .inputs_len = @intCast(args.inputs.len), |
| 13005 | .is_volatile = args.is_volatile, | |
| 13006 | 13006 | }; |
| 13007 | 13007 | |
| 13008 | 13008 | const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); |
src/Sema.zig+25-9| ... | ... | @@ -16388,6 +16388,7 @@ fn zirAsm( |
| 16388 | 16388 | |
| 16389 | 16389 | const pt = sema.pt; |
| 16390 | 16390 | const zcu = pt.zcu; |
| 16391 | const ip = &zcu.intern_pool; | |
| 16391 | 16392 | const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); |
| 16392 | 16393 | const src = block.nodeOffset(extra.data.src_node); |
| 16393 | 16394 | const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node }); |
| ... | ... | @@ -16396,7 +16397,6 @@ fn zirAsm( |
| 16396 | 16397 | const inputs_len = small.inputs_len; |
| 16397 | 16398 | const is_volatile = small.is_volatile; |
| 16398 | 16399 | const is_global_assembly = sema.func_index == .none; |
| 16399 | const zir_tags = sema.code.instructions.items(.tag); | |
| 16400 | 16400 | |
| 16401 | 16401 | const asm_source: []const u8 = if (tmpl_is_expr) s: { |
| 16402 | 16402 | const tmpl: Zir.Inst.Ref = @enumFromInt(@intFromEnum(extra.data.asm_source)); |
| ... | ... | @@ -16426,29 +16426,37 @@ fn zirAsm( |
| 16426 | 16426 | |
| 16427 | 16427 | for (out_args, 0..) |*arg, out_i| { |
| 16428 | 16428 | const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); |
| 16429 | const output_src = block.src(.{ .asm_output = .{ | |
| 16430 | .offset = src.offset.node_offset.x, | |
| 16431 | .output_index = @intCast(out_i), | |
| 16432 | } }); | |
| 16429 | 16433 | extra_i = output.end; |
| 16430 | 16434 | |
| 16431 | 16435 | const is_type = @as(u1, @truncate(output_type_bits)) != 0; |
| 16432 | 16436 | output_type_bits >>= 1; |
| 16433 | 16437 | |
| 16438 | const name = sema.code.nullTerminatedString(output.data.name); | |
| 16439 | ||
| 16434 | 16440 | if (is_type) { |
| 16435 | 16441 | // Indicate the output is the asm instruction return value. |
| 16436 | 16442 | arg.* = .none; |
| 16437 | 16443 | const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); |
| 16438 | 16444 | expr_ty = Air.internedToRef(out_ty.toIntern()); |
| 16439 | 16445 | } else { |
| 16440 | arg.* = try sema.resolveInst(output.data.operand); | |
| 16446 | const inst = try sema.resolveInst(output.data.operand); | |
| 16447 | if (!sema.checkRuntimeValue(inst)) { | |
| 16448 | const output_name = try ip.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls); | |
| 16449 | return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?)); | |
| 16450 | } | |
| 16451 | arg.* = inst; | |
| 16441 | 16452 | } |
| 16442 | 16453 | |
| 16443 | 16454 | const constraint = sema.code.nullTerminatedString(output.data.constraint); |
| 16444 | const name = sema.code.nullTerminatedString(output.data.name); | |
| 16445 | 16455 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; |
| 16446 | 16456 | |
| 16447 | if (output.data.operand.toIndex()) |index| { | |
| 16448 | if (zir_tags[@intFromEnum(index)] == .ref) { | |
| 16449 | // TODO: better error location; it would be even nicer if there were notes that pointed at the output and the variable definition | |
| 16450 | return sema.fail(block, src, "asm cannot output to const local '{s}'", .{name}); | |
| 16451 | } | |
| 16457 | // AstGen gives us a reference to a variable | |
| 16458 | if (arg.* != .none and sema.typeOf(arg.*).isConstPtr(zcu)) { | |
| 16459 | return sema.fail(block, output_src, "asm cannot output to const '{s}'", .{name}); | |
| 16452 | 16460 | } |
| 16453 | 16461 | |
| 16454 | 16462 | outputs[out_i] = .{ .c = constraint, .n = name }; |
| ... | ... | @@ -16459,9 +16467,18 @@ fn zirAsm( |
| 16459 | 16467 | |
| 16460 | 16468 | for (args, 0..) |*arg, arg_i| { |
| 16461 | 16469 | const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); |
| 16470 | const input_src = block.src(.{ .asm_input = .{ | |
| 16471 | .offset = src.offset.node_offset.x, | |
| 16472 | .input_index = @intCast(arg_i), | |
| 16473 | } }); | |
| 16462 | 16474 | extra_i = input.end; |
| 16463 | 16475 | |
| 16464 | 16476 | const uncasted_arg = try sema.resolveInst(input.data.operand); |
| 16477 | const name = sema.code.nullTerminatedString(input.data.name); | |
| 16478 | if (!sema.checkRuntimeValue(uncasted_arg)) { | |
| 16479 | const input_name = try ip.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls); | |
| 16480 | return sema.failWithContainsReferenceToComptimeVar(block, input_src, input_name, "assembly input", .fromInterned(uncasted_arg.toInterned().?)); | |
| 16481 | } | |
| 16465 | 16482 | const uncasted_arg_ty = sema.typeOf(uncasted_arg); |
| 16466 | 16483 | switch (uncasted_arg_ty.zigTypeTag(zcu)) { |
| 16467 | 16484 | .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src), |
| ... | ... | @@ -16472,7 +16489,6 @@ fn zirAsm( |
| 16472 | 16489 | } |
| 16473 | 16490 | |
| 16474 | 16491 | const constraint = sema.code.nullTerminatedString(input.data.constraint); |
| 16475 | const name = sema.code.nullTerminatedString(input.data.name); | |
| 16476 | 16492 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; |
| 16477 | 16493 | inputs[arg_i] = .{ .c = constraint, .n = name }; |
| 16478 | 16494 | } |
src/Zcu.zig+31| ... | ... | @@ -1542,6 +1542,25 @@ pub const SrcLoc = struct { |
| 1542 | 1542 | }; |
| 1543 | 1543 | return tree.nodeToSpan(src_node); |
| 1544 | 1544 | }, |
| 1545 | .asm_input => |input| { | |
| 1546 | const tree = try src_loc.file_scope.getTree(zcu); | |
| 1547 | const node = input.offset.toAbsolute(src_loc.base_node); | |
| 1548 | const full = tree.fullAsm(node).?; | |
| 1549 | const asm_input = full.inputs[input.input_index]; | |
| 1550 | return tree.nodeToSpan(tree.nodeData(asm_input).node_and_token[0]); | |
| 1551 | }, | |
| 1552 | .asm_output => |output| { | |
| 1553 | const tree = try src_loc.file_scope.getTree(zcu); | |
| 1554 | const node = output.offset.toAbsolute(src_loc.base_node); | |
| 1555 | const full = tree.fullAsm(node).?; | |
| 1556 | const asm_output = full.outputs[output.output_index]; | |
| 1557 | const data = tree.nodeData(asm_output).opt_node_and_token; | |
| 1558 | return if (data[0].unwrap()) |output_node| | |
| 1559 | tree.nodeToSpan(output_node) | |
| 1560 | else | |
| 1561 | // token points to the ')' | |
| 1562 | tree.tokenToSpan(data[1] - 1); | |
| 1563 | }, | |
| 1545 | 1564 | .for_input => |for_input| { |
| 1546 | 1565 | const tree = try src_loc.file_scope.getTree(zcu); |
| 1547 | 1566 | const node = for_input.for_node_offset.toAbsolute(src_loc.base_node); |
| ... | ... | @@ -2507,6 +2526,18 @@ pub const LazySrcLoc = struct { |
| 2507 | 2526 | /// The source location points to the operand of a `return` statement, or |
| 2508 | 2527 | /// the `return` itself if there is no explicit operand. |
| 2509 | 2528 | node_offset_return_operand: Ast.Node.Offset, |
| 2529 | /// The source location points to an assembly input | |
| 2530 | asm_input: struct { | |
| 2531 | /// Points to the assembly node | |
| 2532 | offset: Ast.Node.Offset, | |
| 2533 | input_index: u32, | |
| 2534 | }, | |
| 2535 | /// The source location points to an assembly output | |
| 2536 | asm_output: struct { | |
| 2537 | /// Points to the assembly node | |
| 2538 | offset: Ast.Node.Offset, | |
| 2539 | output_index: u32, | |
| 2540 | }, | |
| 2510 | 2541 | /// The source location points to a for loop input. |
| 2511 | 2542 | for_input: struct { |
| 2512 | 2543 | /// Points to the for loop AST node. |
src/print_zir.zig+11-30| ... | ... | @@ -1267,18 +1267,14 @@ const Writer = struct { |
| 1267 | 1267 | tmpl_is_expr: bool, |
| 1268 | 1268 | ) !void { |
| 1269 | 1269 | const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); |
| 1270 | const outputs_len = @as(u5, @truncate(extended.small)); | |
| 1271 | const inputs_len = @as(u5, @truncate(extended.small >> 5)); | |
| 1272 | const clobbers_len = @as(u5, @truncate(extended.small >> 10)); | |
| 1273 | const is_volatile = @as(u1, @truncate(extended.small >> 15)) != 0; | |
| 1270 | const small: Zir.Inst.Asm.Small = @bitCast(extended.small); | |
| 1274 | 1271 | |
| 1275 | try self.writeFlag(stream, "volatile, ", is_volatile); | |
| 1272 | try self.writeFlag(stream, "volatile, ", small.is_volatile); | |
| 1276 | 1273 | if (tmpl_is_expr) { |
| 1277 | 1274 | try self.writeInstRef(stream, @enumFromInt(@intFromEnum(extra.data.asm_source))); |
| 1278 | try stream.writeAll(", "); | |
| 1279 | 1275 | } else { |
| 1280 | 1276 | const asm_source = self.code.nullTerminatedString(extra.data.asm_source); |
| 1281 | try stream.print("\"{f}\", ", .{std.zig.fmtString(asm_source)}); | |
| 1277 | try stream.print("\"{f}\"", .{std.zig.fmtString(asm_source)}); | |
| 1282 | 1278 | } |
| 1283 | 1279 | try stream.writeAll(", "); |
| 1284 | 1280 | |
| ... | ... | @@ -1286,7 +1282,7 @@ const Writer = struct { |
| 1286 | 1282 | var output_type_bits = extra.data.output_type_bits; |
| 1287 | 1283 | { |
| 1288 | 1284 | var i: usize = 0; |
| 1289 | while (i < outputs_len) : (i += 1) { | |
| 1285 | while (i < small.outputs_len) : (i += 1) { | |
| 1290 | 1286 | const output = self.code.extraData(Zir.Inst.Asm.Output, extra_i); |
| 1291 | 1287 | extra_i = output.end; |
| 1292 | 1288 | |
| ... | ... | @@ -1298,17 +1294,14 @@ const Writer = struct { |
| 1298 | 1294 | try stream.print("output({f}, \"{f}\", ", .{ |
| 1299 | 1295 | std.zig.fmtIdP(name), std.zig.fmtString(constraint), |
| 1300 | 1296 | }); |
| 1301 | try self.writeFlag(stream, "->", is_type); | |
| 1297 | try self.writeFlag(stream, "-> ", is_type); | |
| 1302 | 1298 | try self.writeInstRef(stream, output.data.operand); |
| 1303 | try stream.writeAll(")"); | |
| 1304 | if (i + 1 < outputs_len) { | |
| 1305 | try stream.writeAll("), "); | |
| 1306 | } | |
| 1299 | try stream.writeAll("), "); | |
| 1307 | 1300 | } |
| 1308 | 1301 | } |
| 1309 | 1302 | { |
| 1310 | 1303 | var i: usize = 0; |
| 1311 | while (i < inputs_len) : (i += 1) { | |
| 1304 | while (i < small.inputs_len) : (i += 1) { | |
| 1312 | 1305 | const input = self.code.extraData(Zir.Inst.Asm.Input, extra_i); |
| 1313 | 1306 | extra_i = input.end; |
| 1314 | 1307 | |
| ... | ... | @@ -1318,24 +1311,12 @@ const Writer = struct { |
| 1318 | 1311 | std.zig.fmtIdP(name), std.zig.fmtString(constraint), |
| 1319 | 1312 | }); |
| 1320 | 1313 | try self.writeInstRef(stream, input.data.operand); |
| 1321 | try stream.writeAll(")"); | |
| 1322 | if (i + 1 < inputs_len) { | |
| 1323 | try stream.writeAll(", "); | |
| 1324 | } | |
| 1325 | } | |
| 1326 | } | |
| 1327 | { | |
| 1328 | var i: usize = 0; | |
| 1329 | while (i < clobbers_len) : (i += 1) { | |
| 1330 | const str_index = self.code.extra[extra_i]; | |
| 1331 | extra_i += 1; | |
| 1332 | const clobber = self.code.nullTerminatedString(@enumFromInt(str_index)); | |
| 1333 | try stream.print("{f}", .{std.zig.fmtIdP(clobber)}); | |
| 1334 | if (i + 1 < clobbers_len) { | |
| 1335 | try stream.writeAll(", "); | |
| 1336 | } | |
| 1314 | try stream.writeAll("), "); | |
| 1337 | 1315 | } |
| 1338 | 1316 | } |
| 1317 | ||
| 1318 | try self.writeInstRef(stream, extra.data.clobbers); | |
| 1319 | ||
| 1339 | 1320 | try stream.writeAll(")) "); |
| 1340 | 1321 | try self.writeSrcNode(stream, extra.data.src_node); |
| 1341 | 1322 | } |
test/cases/compile_errors/asm_output_to_const.zig+11-5| ... | ... | @@ -1,12 +1,18 @@ |
| 1 | 1 | export fn foo() void { |
| 2 | const f: i64 = 1000; | |
| 2 | const local: usize = 0; | |
| 3 | asm volatile ("" | |
| 4 | : [_] "=r" (local), | |
| 5 | ); | |
| 6 | } | |
| 3 | 7 | |
| 4 | asm volatile ( | |
| 5 | \\ movq $10, %[f] | |
| 6 | : [f] "=r" (f), | |
| 8 | const global: usize = 0; | |
| 9 | export fn bar() void { | |
| 10 | asm volatile ("" | |
| 11 | : [_] "=r" (global), | |
| 7 | 12 | ); |
| 8 | 13 | } |
| 9 | 14 | |
| 10 | 15 | // error |
| 11 | 16 | // |
| 12 | // :4:5: error: asm cannot output to const local 'f' | |
| 17 | // :4:21: error: asm cannot output to const '_' | |
| 18 | // :11:21: error: asm cannot output to const '_' |
test/cases/compile_errors/comptime_var_referenced_by_asm.zig created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | export fn foo() void { | |
| 2 | comptime var a: u32 = 0; | |
| 3 | asm volatile ("" | |
| 4 | : | |
| 5 | : [in] "r" (&a), | |
| 6 | ); | |
| 7 | } | |
| 8 | ||
| 9 | export fn bar() void { | |
| 10 | comptime var a: u32 = 0; | |
| 11 | asm volatile ("" | |
| 12 | : [out] "=r" (a), | |
| 13 | ); | |
| 14 | } | |
| 15 | ||
| 16 | // error | |
| 17 | // | |
| 18 | // :5:21: error: assembly input contains reference to comptime var | |
| 19 | // :2:14: note: 'in' points to comptime var declared here | |
| 20 | // :12:23: error: assembly output contains reference to comptime var | |
| 21 | // :10:14: note: 'out' points to comptime var declared here |