| 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,9 +13000,9 @@ const GenZir = struct { |
| 13000 | } | 13000 | } |
| 13001 | 13001 | ||
| 13002 | const small: Zir.Inst.Asm.Small = .{ | 13002 | const small: Zir.Inst.Asm.Small = .{ |
| 13003 | .is_volatile = args.is_volatile, | ||
| 13003 | .outputs_len = @intCast(args.outputs.len), | 13004 | .outputs_len = @intCast(args.outputs.len), |
| 13004 | .inputs_len = @intCast(args.inputs.len), | 13005 | .inputs_len = @intCast(args.inputs.len), |
| 13005 | .is_volatile = args.is_volatile, | ||
| 13006 | }; | 13006 | }; |
| 13007 | 13007 | ||
| 13008 | const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); | 13008 | const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len); |
src/Sema.zig+25-9| ... | @@ -16388,6 +16388,7 @@ fn zirAsm( | ... | @@ -16388,6 +16388,7 @@ fn zirAsm( |
| 16388 | 16388 | ||
| 16389 | const pt = sema.pt; | 16389 | const pt = sema.pt; |
| 16390 | const zcu = pt.zcu; | 16390 | const zcu = pt.zcu; |
| 16391 | const ip = &zcu.intern_pool; | ||
| 16391 | const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); | 16392 | const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); |
| 16392 | const src = block.nodeOffset(extra.data.src_node); | 16393 | const src = block.nodeOffset(extra.data.src_node); |
| 16393 | const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node }); | 16394 | const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node }); |
| ... | @@ -16396,7 +16397,6 @@ fn zirAsm( | ... | @@ -16396,7 +16397,6 @@ fn zirAsm( |
| 16396 | const inputs_len = small.inputs_len; | 16397 | const inputs_len = small.inputs_len; |
| 16397 | const is_volatile = small.is_volatile; | 16398 | const is_volatile = small.is_volatile; |
| 16398 | const is_global_assembly = sema.func_index == .none; | 16399 | const is_global_assembly = sema.func_index == .none; |
| 16399 | const zir_tags = sema.code.instructions.items(.tag); | ||
| 16400 | 16400 | ||
| 16401 | const asm_source: []const u8 = if (tmpl_is_expr) s: { | 16401 | const asm_source: []const u8 = if (tmpl_is_expr) s: { |
| 16402 | const tmpl: Zir.Inst.Ref = @enumFromInt(@intFromEnum(extra.data.asm_source)); | 16402 | const tmpl: Zir.Inst.Ref = @enumFromInt(@intFromEnum(extra.data.asm_source)); |
| ... | @@ -16426,29 +16426,37 @@ fn zirAsm( | ... | @@ -16426,29 +16426,37 @@ fn zirAsm( |
| 16426 | 16426 | ||
| 16427 | for (out_args, 0..) |*arg, out_i| { | 16427 | for (out_args, 0..) |*arg, out_i| { |
| 16428 | const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); | 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 | extra_i = output.end; | 16433 | extra_i = output.end; |
| 16430 | 16434 | ||
| 16431 | const is_type = @as(u1, @truncate(output_type_bits)) != 0; | 16435 | const is_type = @as(u1, @truncate(output_type_bits)) != 0; |
| 16432 | output_type_bits >>= 1; | 16436 | output_type_bits >>= 1; |
| 16433 | 16437 | ||
| 16438 | const name = sema.code.nullTerminatedString(output.data.name); | ||
| 16439 | |||
| 16434 | if (is_type) { | 16440 | if (is_type) { |
| 16435 | // Indicate the output is the asm instruction return value. | 16441 | // Indicate the output is the asm instruction return value. |
| 16436 | arg.* = .none; | 16442 | arg.* = .none; |
| 16437 | const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); | 16443 | const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); |
| 16438 | expr_ty = Air.internedToRef(out_ty.toIntern()); | 16444 | expr_ty = Air.internedToRef(out_ty.toIntern()); |
| 16439 | } else { | 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 | const constraint = sema.code.nullTerminatedString(output.data.constraint); | 16454 | const constraint = sema.code.nullTerminatedString(output.data.constraint); |
| 16444 | const name = sema.code.nullTerminatedString(output.data.name); | ||
| 16445 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; | 16455 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; |
| 16446 | 16456 | ||
| 16447 | if (output.data.operand.toIndex()) |index| { | 16457 | // AstGen gives us a reference to a variable |
| 16448 | if (zir_tags[@intFromEnum(index)] == .ref) { | 16458 | if (arg.* != .none and sema.typeOf(arg.*).isConstPtr(zcu)) { |
| 16449 | // TODO: better error location; it would be even nicer if there were notes that pointed at the output and the variable definition | 16459 | return sema.fail(block, output_src, "asm cannot output to const '{s}'", .{name}); |
| 16450 | return sema.fail(block, src, "asm cannot output to const local '{s}'", .{name}); | ||
| 16451 | } | ||
| 16452 | } | 16460 | } |
| 16453 | 16461 | ||
| 16454 | outputs[out_i] = .{ .c = constraint, .n = name }; | 16462 | outputs[out_i] = .{ .c = constraint, .n = name }; |
| ... | @@ -16459,9 +16467,18 @@ fn zirAsm( | ... | @@ -16459,9 +16467,18 @@ fn zirAsm( |
| 16459 | 16467 | ||
| 16460 | for (args, 0..) |*arg, arg_i| { | 16468 | for (args, 0..) |*arg, arg_i| { |
| 16461 | const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); | 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 | extra_i = input.end; | 16474 | extra_i = input.end; |
| 16463 | 16475 | ||
| 16464 | const uncasted_arg = try sema.resolveInst(input.data.operand); | 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 | const uncasted_arg_ty = sema.typeOf(uncasted_arg); | 16482 | const uncasted_arg_ty = sema.typeOf(uncasted_arg); |
| 16466 | switch (uncasted_arg_ty.zigTypeTag(zcu)) { | 16483 | switch (uncasted_arg_ty.zigTypeTag(zcu)) { |
| 16467 | .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src), | 16484 | .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src), |
| ... | @@ -16472,7 +16489,6 @@ fn zirAsm( | ... | @@ -16472,7 +16489,6 @@ fn zirAsm( |
| 16472 | } | 16489 | } |
| 16473 | 16490 | ||
| 16474 | const constraint = sema.code.nullTerminatedString(input.data.constraint); | 16491 | const constraint = sema.code.nullTerminatedString(input.data.constraint); |
| 16475 | const name = sema.code.nullTerminatedString(input.data.name); | ||
| 16476 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; | 16492 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; |
| 16477 | inputs[arg_i] = .{ .c = constraint, .n = name }; | 16493 | inputs[arg_i] = .{ .c = constraint, .n = name }; |
| 16478 | } | 16494 | } |
src/Zcu.zig+31| ... | @@ -1542,6 +1542,25 @@ pub const SrcLoc = struct { | ... | @@ -1542,6 +1542,25 @@ pub const SrcLoc = struct { |
| 1542 | }; | 1542 | }; |
| 1543 | return tree.nodeToSpan(src_node); | 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 | .for_input => |for_input| { | 1564 | .for_input => |for_input| { |
| 1546 | const tree = try src_loc.file_scope.getTree(zcu); | 1565 | const tree = try src_loc.file_scope.getTree(zcu); |
| 1547 | const node = for_input.for_node_offset.toAbsolute(src_loc.base_node); | 1566 | const node = for_input.for_node_offset.toAbsolute(src_loc.base_node); |
| ... | @@ -2507,6 +2526,18 @@ pub const LazySrcLoc = struct { | ... | @@ -2507,6 +2526,18 @@ pub const LazySrcLoc = struct { |
| 2507 | /// The source location points to the operand of a `return` statement, or | 2526 | /// The source location points to the operand of a `return` statement, or |
| 2508 | /// the `return` itself if there is no explicit operand. | 2527 | /// the `return` itself if there is no explicit operand. |
| 2509 | node_offset_return_operand: Ast.Node.Offset, | 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 | /// The source location points to a for loop input. | 2541 | /// The source location points to a for loop input. |
| 2511 | for_input: struct { | 2542 | for_input: struct { |
| 2512 | /// Points to the for loop AST node. | 2543 | /// Points to the for loop AST node. |
src/print_zir.zig+11-30| ... | @@ -1267,18 +1267,14 @@ const Writer = struct { | ... | @@ -1267,18 +1267,14 @@ const Writer = struct { |
| 1267 | tmpl_is_expr: bool, | 1267 | tmpl_is_expr: bool, |
| 1268 | ) !void { | 1268 | ) !void { |
| 1269 | const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); | 1269 | const extra = self.code.extraData(Zir.Inst.Asm, extended.operand); |
| 1270 | const outputs_len = @as(u5, @truncate(extended.small)); | 1270 | const small: Zir.Inst.Asm.Small = @bitCast(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; | ||
| 1274 | 1271 | ||
| 1275 | try self.writeFlag(stream, "volatile, ", is_volatile); | 1272 | try self.writeFlag(stream, "volatile, ", small.is_volatile); |
| 1276 | if (tmpl_is_expr) { | 1273 | if (tmpl_is_expr) { |
| 1277 | try self.writeInstRef(stream, @enumFromInt(@intFromEnum(extra.data.asm_source))); | 1274 | try self.writeInstRef(stream, @enumFromInt(@intFromEnum(extra.data.asm_source))); |
| 1278 | try stream.writeAll(", "); | ||
| 1279 | } else { | 1275 | } else { |
| 1280 | const asm_source = self.code.nullTerminatedString(extra.data.asm_source); | 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 | try stream.writeAll(", "); | 1279 | try stream.writeAll(", "); |
| 1284 | 1280 | ||
| ... | @@ -1286,7 +1282,7 @@ const Writer = struct { | ... | @@ -1286,7 +1282,7 @@ const Writer = struct { |
| 1286 | var output_type_bits = extra.data.output_type_bits; | 1282 | var output_type_bits = extra.data.output_type_bits; |
| 1287 | { | 1283 | { |
| 1288 | var i: usize = 0; | 1284 | var i: usize = 0; |
| 1289 | while (i < outputs_len) : (i += 1) { | 1285 | while (i < small.outputs_len) : (i += 1) { |
| 1290 | const output = self.code.extraData(Zir.Inst.Asm.Output, extra_i); | 1286 | const output = self.code.extraData(Zir.Inst.Asm.Output, extra_i); |
| 1291 | extra_i = output.end; | 1287 | extra_i = output.end; |
| 1292 | 1288 | ||
| ... | @@ -1298,17 +1294,14 @@ const Writer = struct { | ... | @@ -1298,17 +1294,14 @@ const Writer = struct { |
| 1298 | try stream.print("output({f}, \"{f}\", ", .{ | 1294 | try stream.print("output({f}, \"{f}\", ", .{ |
| 1299 | std.zig.fmtIdP(name), std.zig.fmtString(constraint), | 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 | try self.writeInstRef(stream, output.data.operand); | 1298 | try self.writeInstRef(stream, output.data.operand); |
| 1303 | try stream.writeAll(")"); | 1299 | try stream.writeAll("), "); |
| 1304 | if (i + 1 < outputs_len) { | ||
| 1305 | try stream.writeAll("), "); | ||
| 1306 | } | ||
| 1307 | } | 1300 | } |
| 1308 | } | 1301 | } |
| 1309 | { | 1302 | { |
| 1310 | var i: usize = 0; | 1303 | var i: usize = 0; |
| 1311 | while (i < inputs_len) : (i += 1) { | 1304 | while (i < small.inputs_len) : (i += 1) { |
| 1312 | const input = self.code.extraData(Zir.Inst.Asm.Input, extra_i); | 1305 | const input = self.code.extraData(Zir.Inst.Asm.Input, extra_i); |
| 1313 | extra_i = input.end; | 1306 | extra_i = input.end; |
| 1314 | 1307 | ||
| ... | @@ -1318,24 +1311,12 @@ const Writer = struct { | ... | @@ -1318,24 +1311,12 @@ const Writer = struct { |
| 1318 | std.zig.fmtIdP(name), std.zig.fmtString(constraint), | 1311 | std.zig.fmtIdP(name), std.zig.fmtString(constraint), |
| 1319 | }); | 1312 | }); |
| 1320 | try self.writeInstRef(stream, input.data.operand); | 1313 | try self.writeInstRef(stream, input.data.operand); |
| 1321 | try stream.writeAll(")"); | 1314 | 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 | } | ||
| 1337 | } | 1315 | } |
| 1338 | } | 1316 | } |
| 1317 | |||
| 1318 | try self.writeInstRef(stream, extra.data.clobbers); | ||
| 1319 | |||
| 1339 | try stream.writeAll(")) "); | 1320 | try stream.writeAll(")) "); |
| 1340 | try self.writeSrcNode(stream, extra.data.src_node); | 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,12 +1,18 @@ |
| 1 | export fn foo() void { | 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 ( | 8 | const global: usize = 0; |
| 5 | \\ movq $10, %[f] | 9 | export fn bar() void { |
| 6 | : [f] "=r" (f), | 10 | asm volatile ("" |
| 11 | : [_] "=r" (global), | ||
| 7 | ); | 12 | ); |
| 8 | } | 13 | } |
| 9 | 14 | ||
| 10 | // error | 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 | ||