authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-10 02:35:22-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-10 02:35:22-05:00
log03adafd8023691af6a1e3d784a6e7e1f77d46859
tree5add5d1c023d3973996c58406d44f701249be8ff
parentc550eb3e8ab7ae77e5533313c219b2015e633081
parenta67d3785435ad6cbd57fca2e9247ad5ff3edfa7e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17947 from jacobly0/fwd-clang-errs

Compilation: forward clang diagnostics to error bundles

3 files changed, 883 insertions(+), 65 deletions(-)

src/Compilation.zig+325-64
......@@ -77,7 +77,7 @@ embed_file_work_queue: std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic),
7777
7878/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
7979/// This data is accessed by multiple threads and is protected by `mutex`.
80failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.ErrorMsg) = .{},
80failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.Diag.Bundle) = .{},
8181
8282/// The ErrorBundle memory is owned by the `Win32Resource`, using Compilation's general purpose allocator.
8383/// This data is accessed by multiple threads and is protected by `mutex`.
......@@ -318,15 +318,286 @@ pub const CObject = struct {
318318 failure_retryable,
319319 },
320320
321 pub const ErrorMsg = struct {
322 msg: []const u8,
323 line: u32,
324 column: u32,
321 pub const Diag = struct {
322 level: u32 = 0,
323 category: u32 = 0,
324 msg: []const u8 = &.{},
325 src_loc: SrcLoc = .{},
326 src_ranges: []const SrcRange = &.{},
327 sub_diags: []const Diag = &.{},
328
329 pub const SrcLoc = struct {
330 file: u32 = 0,
331 line: u32 = 0,
332 column: u32 = 0,
333 offset: u32 = 0,
334 };
335
336 pub const SrcRange = struct {
337 start: SrcLoc = .{},
338 end: SrcLoc = .{},
339 };
340
341 pub fn deinit(diag: *Diag, gpa: Allocator) void {
342 gpa.free(diag.msg);
343 gpa.free(diag.src_ranges);
344 for (diag.sub_diags) |sub_diag| {
345 var sub_diag_mut = sub_diag;
346 sub_diag_mut.deinit(gpa);
347 }
348 gpa.free(diag.sub_diags);
349 diag.* = undefined;
350 }
325351
326 pub fn destroy(em: *ErrorMsg, gpa: Allocator) void {
327 gpa.free(em.msg);
328 gpa.destroy(em);
352 pub fn count(diag: Diag) u32 {
353 var total: u32 = 1;
354 for (diag.sub_diags) |sub_diag| total += sub_diag.count();
355 return total;
329356 }
357
358 pub fn addToErrorBundle(diag: Diag, eb: *ErrorBundle.Wip, bundle: Bundle, note: *u32) !void {
359 const err_msg = try eb.addErrorMessage(try diag.toErrorMessage(eb, bundle, 0));
360 eb.extra.items[note.*] = @intFromEnum(err_msg);
361 note.* += 1;
362 for (diag.sub_diags) |sub_diag| try sub_diag.addToErrorBundle(eb, bundle, note);
363 }
364
365 pub fn toErrorMessage(
366 diag: Diag,
367 eb: *ErrorBundle.Wip,
368 bundle: Bundle,
369 notes_len: u32,
370 ) !ErrorBundle.ErrorMessage {
371 var start = diag.src_loc.offset;
372 var end = diag.src_loc.offset;
373 for (diag.src_ranges) |src_range| {
374 if (src_range.start.file == diag.src_loc.file and
375 src_range.start.line == diag.src_loc.line)
376 {
377 start = @min(src_range.start.offset, start);
378 }
379 if (src_range.end.file == diag.src_loc.file and
380 src_range.end.line == diag.src_loc.line)
381 {
382 end = @max(src_range.end.offset, end);
383 }
384 }
385
386 const file_name = bundle.file_names.get(diag.src_loc.file) orelse "";
387 const source_line = source_line: {
388 if (diag.src_loc.offset == 0 or diag.src_loc.column == 0) break :source_line 0;
389
390 const file = std.fs.cwd().openFile(file_name, .{}) catch break :source_line 0;
391 defer file.close();
392 file.seekTo(diag.src_loc.offset + 1 - diag.src_loc.column) catch break :source_line 0;
393
394 var line = std.ArrayList(u8).init(eb.gpa);
395 defer line.deinit();
396 file.reader().readUntilDelimiterArrayList(&line, '\n', 1 << 10) catch break :source_line 0;
397
398 break :source_line try eb.addString(line.items);
399 };
400
401 return .{
402 .msg = try eb.addString(diag.msg),
403 .src_loc = try eb.addSourceLocation(.{
404 .src_path = try eb.addString(file_name),
405 .line = diag.src_loc.line -| 1,
406 .column = diag.src_loc.column -| 1,
407 .span_start = start,
408 .span_main = diag.src_loc.offset,
409 .span_end = end + 1,
410 .source_line = source_line,
411 }),
412 .notes_len = notes_len,
413 };
414 }
415
416 pub const Bundle = struct {
417 file_names: std.AutoHashMapUnmanaged(u32, []const u8) = .{},
418 category_names: std.AutoHashMapUnmanaged(u32, []const u8) = .{},
419 diags: []Diag = &.{},
420
421 pub fn destroy(bundle: *Bundle, gpa: Allocator) void {
422 var file_name_it = bundle.file_names.valueIterator();
423 while (file_name_it.next()) |file_name| gpa.free(file_name.*);
424 bundle.file_names.deinit(gpa);
425
426 var category_name_it = bundle.category_names.valueIterator();
427 while (category_name_it.next()) |category_name| gpa.free(category_name.*);
428 bundle.category_names.deinit(gpa);
429
430 for (bundle.diags) |*diag| diag.deinit(gpa);
431 gpa.free(bundle.diags);
432
433 gpa.destroy(bundle);
434 }
435
436 pub fn parse(gpa: Allocator, path: []const u8) !*Bundle {
437 const BitcodeReader = @import("codegen/llvm/BitcodeReader.zig");
438 const BlockId = enum(u32) {
439 Meta = 8,
440 Diag,
441 _,
442 };
443 const RecordId = enum(u32) {
444 Version = 1,
445 DiagInfo,
446 SrcRange,
447 DiagFlag,
448 CatName,
449 FileName,
450 FixIt,
451 _,
452 };
453 const WipDiag = struct {
454 level: u32 = 0,
455 category: u32 = 0,
456 msg: []const u8 = &.{},
457 src_loc: SrcLoc = .{},
458 src_ranges: std.ArrayListUnmanaged(SrcRange) = .{},
459 sub_diags: std.ArrayListUnmanaged(Diag) = .{},
460
461 fn deinit(wip_diag: *@This(), allocator: Allocator) void {
462 allocator.free(wip_diag.msg);
463 wip_diag.src_ranges.deinit(allocator);
464 for (wip_diag.sub_diags.items) |*sub_diag| sub_diag.deinit(allocator);
465 wip_diag.sub_diags.deinit(allocator);
466 wip_diag.* = undefined;
467 }
468 };
469
470 const file = try std.fs.cwd().openFile(path, .{});
471 defer file.close();
472 var br = std.io.bufferedReader(file.reader());
473 const reader = br.reader();
474 var bc = BitcodeReader.init(gpa, .{ .reader = reader.any() });
475 defer bc.deinit();
476
477 var file_names: std.AutoHashMapUnmanaged(u32, []const u8) = .{};
478 errdefer {
479 var file_name_it = file_names.valueIterator();
480 while (file_name_it.next()) |file_name| gpa.free(file_name.*);
481 file_names.deinit(gpa);
482 }
483
484 var category_names: std.AutoHashMapUnmanaged(u32, []const u8) = .{};
485 errdefer {
486 var category_name_it = category_names.valueIterator();
487 while (category_name_it.next()) |category_name| gpa.free(category_name.*);
488 category_names.deinit(gpa);
489 }
490
491 var stack: std.ArrayListUnmanaged(WipDiag) = .{};
492 defer {
493 for (stack.items) |*wip_diag| wip_diag.deinit(gpa);
494 stack.deinit(gpa);
495 }
496 try stack.append(gpa, .{});
497
498 try bc.checkMagic("DIAG");
499 while (try bc.next()) |item| switch (item) {
500 .start_block => |block| switch (@as(BlockId, @enumFromInt(block.id))) {
501 .Meta => if (stack.items.len > 0) try bc.skipBlock(block),
502 .Diag => try stack.append(gpa, .{}),
503 _ => try bc.skipBlock(block),
504 },
505 .record => |record| switch (@as(RecordId, @enumFromInt(record.id))) {
506 .Version => if (record.operands[0] != 2) return error.InvalidVersion,
507 .DiagInfo => {
508 const top = &stack.items[stack.items.len - 1];
509 top.level = @intCast(record.operands[0]);
510 top.src_loc = .{
511 .file = @intCast(record.operands[1]),
512 .line = @intCast(record.operands[2]),
513 .column = @intCast(record.operands[3]),
514 .offset = @intCast(record.operands[4]),
515 };
516 top.category = @intCast(record.operands[5]);
517 top.msg = try gpa.dupe(u8, record.blob);
518 },
519 .SrcRange => try stack.items[stack.items.len - 1].src_ranges.append(gpa, .{
520 .start = .{
521 .file = @intCast(record.operands[0]),
522 .line = @intCast(record.operands[1]),
523 .column = @intCast(record.operands[2]),
524 .offset = @intCast(record.operands[3]),
525 },
526 .end = .{
527 .file = @intCast(record.operands[4]),
528 .line = @intCast(record.operands[5]),
529 .column = @intCast(record.operands[6]),
530 .offset = @intCast(record.operands[7]),
531 },
532 }),
533 .DiagFlag => {},
534 .CatName => {
535 try category_names.ensureUnusedCapacity(gpa, 1);
536 category_names.putAssumeCapacity(
537 @intCast(record.operands[0]),
538 try gpa.dupe(u8, record.blob),
539 );
540 },
541 .FileName => {
542 try file_names.ensureUnusedCapacity(gpa, 1);
543 file_names.putAssumeCapacity(
544 @intCast(record.operands[0]),
545 try gpa.dupe(u8, record.blob),
546 );
547 },
548 .FixIt => {},
549 _ => {},
550 },
551 .end_block => |block| switch (@as(BlockId, @enumFromInt(block.id))) {
552 .Meta => {},
553 .Diag => {
554 var wip_diag = stack.pop();
555 errdefer wip_diag.deinit(gpa);
556
557 const src_ranges = try wip_diag.src_ranges.toOwnedSlice(gpa);
558 errdefer gpa.free(src_ranges);
559
560 const sub_diags = try wip_diag.sub_diags.toOwnedSlice(gpa);
561 errdefer {
562 for (sub_diags) |*sub_diag| sub_diag.deinit(gpa);
563 gpa.free(sub_diags);
564 }
565
566 try stack.items[stack.items.len - 1].sub_diags.append(gpa, .{
567 .level = wip_diag.level,
568 .category = wip_diag.category,
569 .msg = wip_diag.msg,
570 .src_loc = wip_diag.src_loc,
571 .src_ranges = src_ranges,
572 .sub_diags = sub_diags,
573 });
574 },
575 _ => {},
576 },
577 };
578
579 const bundle = try gpa.create(Bundle);
580 assert(stack.items.len == 1);
581 bundle.* = .{
582 .file_names = file_names,
583 .category_names = category_names,
584 .diags = try stack.items[0].sub_diags.toOwnedSlice(gpa),
585 };
586 return bundle;
587 }
588
589 pub fn addToErrorBundle(bundle: Bundle, eb: *ErrorBundle.Wip) !void {
590 for (bundle.diags) |diag| {
591 const notes_len = diag.count() - 1;
592 try eb.addRootErrorMessage(try diag.toErrorMessage(eb, bundle, notes_len));
593 if (notes_len > 0) {
594 var note = try eb.reserveNotes(notes_len);
595 for (diag.sub_diags) |sub_diag|
596 try sub_diag.addToErrorBundle(eb, bundle, &note);
597 }
598 }
599 }
600 };
330601 };
331602
332603 /// Returns if there was failure.
......@@ -2826,11 +3097,16 @@ fn addBuf(bufs_list: []std.os.iovec_const, bufs_len: *usize, buf: []const u8) vo
28263097
28273098/// This function is temporally single-threaded.
28283099pub fn totalErrorCount(self: *Compilation) u32 {
2829 var total: usize = self.failed_c_objects.count() +
3100 var total: usize =
28303101 self.misc_failures.count() +
28313102 @intFromBool(self.alloc_failure_occurred) +
28323103 self.lld_errors.items.len;
28333104
3105 {
3106 var it = self.failed_c_objects.iterator();
3107 while (it.next()) |entry| total += entry.value_ptr.*.diags.len;
3108 }
3109
28343110 if (!build_options.only_core_functionality) {
28353111 for (self.failed_win32_resources.values()) |errs| {
28363112 total += errs.errorMessageCount();
......@@ -2911,24 +3187,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
29113187
29123188 {
29133189 var it = self.failed_c_objects.iterator();
2914 while (it.next()) |entry| {
2915 const c_object = entry.key_ptr.*;
2916 const err_msg = entry.value_ptr.*;
2917 // TODO these fields will need to be adjusted when we have proper
2918 // C error reporting bubbling up.
2919 try bundle.addRootErrorMessage(.{
2920 .msg = try bundle.printString("unable to build C object: {s}", .{err_msg.msg}),
2921 .src_loc = try bundle.addSourceLocation(.{
2922 .src_path = try bundle.addString(c_object.src.src_path),
2923 .span_start = 0,
2924 .span_main = 0,
2925 .span_end = 1,
2926 .line = err_msg.line,
2927 .column = err_msg.column,
2928 .source_line = 0, // TODO
2929 }),
2930 });
2931 }
3190 while (it.next()) |entry| try entry.value_ptr.*.addToErrorBundle(&bundle);
29323191 }
29333192
29343193 if (!build_options.only_core_functionality) {
......@@ -4209,19 +4468,9 @@ fn reportRetryableCObjectError(
42094468) error{OutOfMemory}!void {
42104469 c_object.status = .failure_retryable;
42114470
4212 const c_obj_err_msg = try comp.gpa.create(CObject.ErrorMsg);
4213 errdefer comp.gpa.destroy(c_obj_err_msg);
4214 const msg = try std.fmt.allocPrint(comp.gpa, "{s}", .{@errorName(err)});
4215 errdefer comp.gpa.free(msg);
4216 c_obj_err_msg.* = .{
4217 .msg = msg,
4218 .line = 0,
4219 .column = 0,
4220 };
4221 {
4222 comp.mutex.lock();
4223 defer comp.mutex.unlock();
4224 try comp.failed_c_objects.putNoClobber(comp.gpa, c_object, c_obj_err_msg);
4471 switch (comp.failCObj(c_object, "{s}", .{@errorName(err)})) {
4472 error.AnalysisFail => return,
4473 else => |e| return e,
42254474 }
42264475}
42274476
......@@ -4457,6 +4706,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
44574706 // We can't know the digest until we do the C compiler invocation,
44584707 // so we need a temporary filename.
44594708 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
4709 const out_diag_path = try std.fmt.allocPrint(arena, "{s}.diag", .{out_obj_path});
44604710 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
44614711 defer zig_cache_tmp_dir.close();
44624712
......@@ -4470,18 +4720,20 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
44704720
44714721 try argv.ensureUnusedCapacity(5);
44724722 switch (comp.clang_preprocessor_mode) {
4473 .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }),
4474 .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }),
4723 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
4724 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
44754725 .stdout => argv.appendAssumeCapacity("-E"),
44764726 }
44774727 if (comp.clang_passthrough_mode) {
44784728 if (comp.emit_asm != null) {
44794729 argv.appendAssumeCapacity("-S");
44804730 } else if (comp.emit_llvm_ir != null) {
4481 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" });
4731 argv.appendSliceAssumeCapacity(&.{ "-emit-llvm", "-S" });
44824732 } else if (comp.emit_llvm_bc != null) {
44834733 argv.appendAssumeCapacity("-emit-llvm");
44844734 }
4735 } else {
4736 argv.appendSliceAssumeCapacity(&.{ "--serialize-diagnostics", out_diag_path });
44854737 }
44864738
44874739 if (comp.verbose_cc) {
......@@ -4524,10 +4776,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
45244776 switch (term) {
45254777 .Exited => |code| {
45264778 if (code != 0) {
4527 // TODO parse clang stderr and turn it into an error message
4528 // and then call failCObjWithOwnedErrorMsg
4529 log.err("clang failed with stderr: {s}", .{stderr});
4530 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
4779 const bundle = CObject.Diag.Bundle.parse(comp.gpa, out_diag_path) catch |err| {
4780 log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr });
4781 return comp.failCObj(c_object, "clang exited with code {d}", .{code});
4782 };
4783 return comp.failCObjWithOwnedDiagBundle(c_object, bundle);
45314784 }
45324785 },
45334786 else => {
......@@ -5413,37 +5666,45 @@ pub fn addCCArgs(
54135666 try argv.appendSlice(comp.clang_argv);
54145667}
54155668
5416fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) SemaError {
5669fn failCObj(
5670 comp: *Compilation,
5671 c_object: *CObject,
5672 comptime format: []const u8,
5673 args: anytype,
5674) SemaError {
54175675 @setCold(true);
5418 const err_msg = blk: {
5419 const msg = try std.fmt.allocPrint(comp.gpa, format, args);
5420 errdefer comp.gpa.free(msg);
5421 const err_msg = try comp.gpa.create(CObject.ErrorMsg);
5422 errdefer comp.gpa.destroy(err_msg);
5423 err_msg.* = .{
5424 .msg = msg,
5425 .line = 0,
5426 .column = 0,
5427 };
5428 break :blk err_msg;
5676 const diag_bundle = blk: {
5677 const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle);
5678 diag_bundle.* = .{};
5679 errdefer diag_bundle.destroy(comp.gpa);
5680
5681 try diag_bundle.file_names.ensureTotalCapacity(comp.gpa, 1);
5682 diag_bundle.file_names.putAssumeCapacity(1, try comp.gpa.dupe(u8, c_object.src.src_path));
5683
5684 diag_bundle.diags = try comp.gpa.alloc(CObject.Diag, 1);
5685 diag_bundle.diags[0] = .{};
5686 diag_bundle.diags[0].level = 3;
5687 diag_bundle.diags[0].msg = try std.fmt.allocPrint(comp.gpa, format, args);
5688 diag_bundle.diags[0].src_loc.file = 1;
5689 break :blk diag_bundle;
54295690 };
5430 return comp.failCObjWithOwnedErrorMsg(c_object, err_msg);
5691 return comp.failCObjWithOwnedDiagBundle(c_object, diag_bundle);
54315692}
54325693
5433fn failCObjWithOwnedErrorMsg(
5694fn failCObjWithOwnedDiagBundle(
54345695 comp: *Compilation,
54355696 c_object: *CObject,
5436 err_msg: *CObject.ErrorMsg,
5697 diag_bundle: *CObject.Diag.Bundle,
54375698) SemaError {
54385699 @setCold(true);
54395700 {
54405701 comp.mutex.lock();
54415702 defer comp.mutex.unlock();
54425703 {
5443 errdefer err_msg.destroy(comp.gpa);
5704 errdefer diag_bundle.destroy(comp.gpa);
54445705 try comp.failed_c_objects.ensureUnusedCapacity(comp.gpa, 1);
54455706 }
5446 comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg);
5707 comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, diag_bundle);
54475708 }
54485709 c_object.status = .failure;
54495710 return error.AnalysisFail;
src/codegen/llvm/BitcodeReader.zig created+515
......@@ -0,0 +1,515 @@
1allocator: std.mem.Allocator,
2record_arena: std.heap.ArenaAllocator.State,
3reader: std.io.AnyReader,
4keep_names: bool,
5bit_buffer: u32,
6bit_offset: u5,
7stack: std.ArrayListUnmanaged(State),
8block_info: std.AutoHashMapUnmanaged(u32, Block.Info),
9
10pub const Item = union(enum) {
11 start_block: Block,
12 record: Record,
13 end_block: Block,
14};
15
16pub const Block = struct {
17 name: []const u8,
18 id: u32,
19 len: u32,
20
21 const block_info: u32 = 0;
22 const first_reserved: u32 = 1;
23 const last_standard: u32 = 7;
24
25 const Info = struct {
26 block_name: []const u8,
27 record_names: std.AutoHashMapUnmanaged(u32, []const u8),
28 abbrevs: Abbrev.Store,
29
30 const default: Info = .{
31 .block_name = &.{},
32 .record_names = .{},
33 .abbrevs = .{ .abbrevs = .{} },
34 };
35
36 const set_bid: u32 = 1;
37 const block_name: u32 = 2;
38 const set_record_name: u32 = 3;
39
40 fn deinit(info: *Info, allocator: std.mem.Allocator) void {
41 allocator.free(info.block_name);
42 var record_names_it = info.record_names.valueIterator();
43 while (record_names_it.next()) |record_name| allocator.free(record_name.*);
44 info.record_names.deinit(allocator);
45 info.abbrevs.deinit(allocator);
46 info.* = undefined;
47 }
48 };
49};
50
51pub const Record = struct {
52 name: []const u8,
53 id: u32,
54 operands: []u64,
55 blob: []u8,
56
57 fn toOwnedAbbrev(record: Record, allocator: std.mem.Allocator) !Abbrev {
58 var operands = std.ArrayList(Abbrev.Operand).init(allocator);
59 defer operands.deinit();
60
61 assert(record.id == Abbrev.Builtin.define_abbrev.toRecordId());
62 var i: usize = 0;
63 while (i < record.operands.len) switch (record.operands[i]) {
64 Abbrev.Operand.literal => {
65 try operands.append(.{ .literal = record.operands[i + 1] });
66 i += 2;
67 },
68 @intFromEnum(Abbrev.Operand.Encoding.fixed) => {
69 try operands.append(.{ .encoding = .{ .fixed = @intCast(record.operands[i + 1]) } });
70 i += 2;
71 },
72 @intFromEnum(Abbrev.Operand.Encoding.vbr) => {
73 try operands.append(.{ .encoding = .{ .vbr = @intCast(record.operands[i + 1]) } });
74 i += 2;
75 },
76 @intFromEnum(Abbrev.Operand.Encoding.array) => {
77 try operands.append(.{ .encoding = .{ .array = 6 } });
78 i += 1;
79 },
80 @intFromEnum(Abbrev.Operand.Encoding.char6) => {
81 try operands.append(.{ .encoding = .char6 });
82 i += 1;
83 },
84 @intFromEnum(Abbrev.Operand.Encoding.blob) => {
85 try operands.append(.{ .encoding = .{ .blob = 6 } });
86 i += 1;
87 },
88 else => unreachable,
89 };
90
91 return .{ .operands = try operands.toOwnedSlice() };
92 }
93};
94
95pub const InitOptions = struct {
96 reader: std.io.AnyReader,
97 keep_names: bool = false,
98};
99pub fn init(allocator: std.mem.Allocator, options: InitOptions) BitcodeReader {
100 return .{
101 .allocator = allocator,
102 .record_arena = .{},
103 .reader = options.reader,
104 .keep_names = options.keep_names,
105 .bit_buffer = 0,
106 .bit_offset = 0,
107 .stack = .{},
108 .block_info = .{},
109 };
110}
111
112pub fn deinit(bc: *BitcodeReader) void {
113 var block_info_it = bc.block_info.valueIterator();
114 while (block_info_it.next()) |block_info| block_info.deinit(bc.allocator);
115 bc.block_info.deinit(bc.allocator);
116 for (bc.stack.items) |*state| state.deinit(bc.allocator);
117 bc.stack.deinit(bc.allocator);
118 bc.record_arena.promote(bc.allocator).deinit();
119 bc.* = undefined;
120}
121
122pub fn checkMagic(bc: *BitcodeReader, magic: *const [4]u8) !void {
123 var buffer: [4]u8 = undefined;
124 try bc.readBytes(&buffer);
125 if (!std.mem.eql(u8, &buffer, magic)) return error.InvalidMagic;
126
127 try bc.startBlock(null, 2);
128 try bc.block_info.put(bc.allocator, Block.block_info, Block.Info.default);
129}
130
131pub fn next(bc: *BitcodeReader) !?Item {
132 while (true) {
133 const record = (try bc.nextRecord()) orelse
134 return if (bc.stack.items.len > 1) error.EndOfStream else null;
135 switch (record.id) {
136 else => return .{ .record = record },
137 Abbrev.Builtin.end_block.toRecordId() => {
138 const block_id = bc.stack.items[bc.stack.items.len - 1].block_id.?;
139 try bc.endBlock();
140 return .{ .end_block = .{
141 .name = if (bc.block_info.get(block_id)) |block_info|
142 block_info.block_name
143 else
144 &.{},
145 .id = block_id,
146 .len = 0,
147 } };
148 },
149 Abbrev.Builtin.enter_subblock.toRecordId() => {
150 const block_id: u32 = @intCast(record.operands[0]);
151 switch (block_id) {
152 Block.block_info => try bc.parseBlockInfoBlock(),
153 Block.first_reserved...Block.last_standard => return error.UnsupportedBlockId,
154 else => {
155 try bc.startBlock(block_id, @intCast(record.operands[1]));
156 return .{ .start_block = .{
157 .name = if (bc.block_info.get(block_id)) |block_info|
158 block_info.block_name
159 else
160 &.{},
161 .id = block_id,
162 .len = @intCast(record.operands[2]),
163 } };
164 },
165 }
166 },
167 Abbrev.Builtin.define_abbrev.toRecordId() => try bc.stack.items[bc.stack.items.len - 1]
168 .abbrevs.addOwnedAbbrev(bc.allocator, try record.toOwnedAbbrev(bc.allocator)),
169 }
170 }
171}
172
173pub fn skipBlock(bc: *BitcodeReader, block: Block) !void {
174 assert(bc.bit_offset == 0);
175 try bc.reader.skipBytes(@as(u34, block.len) * 4, .{});
176 try bc.endBlock();
177}
178
179fn nextRecord(bc: *BitcodeReader) !?Record {
180 const state = &bc.stack.items[bc.stack.items.len - 1];
181 const abbrev_id = bc.readFixed(u32, state.abbrev_id_width) catch |err| switch (err) {
182 error.EndOfStream => return null,
183 else => |e| return e,
184 };
185 if (abbrev_id >= state.abbrevs.abbrevs.items.len) return error.InvalidAbbrevId;
186 const abbrev = state.abbrevs.abbrevs.items[abbrev_id];
187
188 var record_arena = bc.record_arena.promote(bc.allocator);
189 defer bc.record_arena = record_arena.state;
190 _ = record_arena.reset(.retain_capacity);
191
192 var operands = try std.ArrayList(u64).initCapacity(record_arena.allocator(), abbrev.operands.len);
193 var blob = std.ArrayList(u8).init(record_arena.allocator());
194 for (abbrev.operands, 0..) |abbrev_operand, abbrev_operand_i| switch (abbrev_operand) {
195 .literal => |value| operands.appendAssumeCapacity(value),
196 .encoding => |abbrev_encoding| switch (abbrev_encoding) {
197 .fixed => |width| operands.appendAssumeCapacity(try bc.readFixed(u64, width)),
198 .vbr => |width| operands.appendAssumeCapacity(try bc.readVbr(u64, width)),
199 .array => |len_width| {
200 assert(abbrev_operand_i + 2 == abbrev.operands.len);
201 const len: usize = @intCast(try bc.readVbr(u32, len_width));
202 try operands.ensureUnusedCapacity(len);
203 for (0..len) |_| switch (abbrev.operands[abbrev.operands.len - 1]) {
204 .literal => |elem_value| operands.appendAssumeCapacity(elem_value),
205 .encoding => |elem_encoding| switch (elem_encoding) {
206 .fixed => |elem_width| operands.appendAssumeCapacity(try bc.readFixed(u64, elem_width)),
207 .vbr => |elem_width| operands.appendAssumeCapacity(try bc.readVbr(u64, elem_width)),
208 .array, .blob => return error.InvalidArrayElement,
209 .char6 => operands.appendAssumeCapacity(try bc.readChar6()),
210 },
211 .align_32_bits, .block_len => return error.UnsupportedArrayElement,
212 .abbrev_op => switch (try bc.readFixed(u1, 1)) {
213 1 => try operands.appendSlice(&.{
214 Abbrev.Operand.literal,
215 try bc.readVbr(u64, 8),
216 }),
217 0 => {
218 const encoding: Abbrev.Operand.Encoding =
219 @enumFromInt(try bc.readFixed(u3, 3));
220 try operands.append(@intFromEnum(encoding));
221 switch (encoding) {
222 .fixed, .vbr => try operands.append(try bc.readVbr(u7, 5)),
223 .array, .char6, .blob => {},
224 _ => return error.UnsuportedAbbrevEncoding,
225 }
226 },
227 },
228 };
229 break;
230 },
231 .char6 => operands.appendAssumeCapacity(try bc.readChar6()),
232 .blob => |len_width| {
233 assert(abbrev_operand_i + 1 == abbrev.operands.len);
234 const len = std.math.cast(usize, try bc.readVbr(u32, len_width)) orelse
235 return error.Overflow;
236 bc.align32Bits();
237 try bc.readBytes(try blob.addManyAsSlice(len));
238 bc.align32Bits();
239 },
240 },
241 .align_32_bits => bc.align32Bits(),
242 .block_len => operands.appendAssumeCapacity(try bc.read32Bits()),
243 .abbrev_op => unreachable,
244 };
245 return .{
246 .name = name: {
247 if (operands.items.len < 1) break :name &.{};
248 const record_id = std.math.cast(u32, operands.items[0]) orelse break :name &.{};
249 if (state.block_id) |block_id| {
250 if (bc.block_info.get(block_id)) |block_info| {
251 break :name block_info.record_names.get(record_id) orelse break :name &.{};
252 }
253 }
254 break :name &.{};
255 },
256 .id = std.math.cast(u32, operands.items[0]) orelse return error.InvalidRecordId,
257 .operands = operands.items[1..],
258 .blob = blob.items,
259 };
260}
261
262fn startBlock(bc: *BitcodeReader, block_id: ?u32, new_abbrev_len: u6) !void {
263 const abbrevs = if (block_id) |id|
264 if (bc.block_info.get(id)) |block_info| block_info.abbrevs.abbrevs.items else &.{}
265 else
266 &.{};
267
268 const state = try bc.stack.addOne(bc.allocator);
269 state.* = .{
270 .block_id = block_id,
271 .abbrev_id_width = new_abbrev_len,
272 .abbrevs = .{ .abbrevs = .{} },
273 };
274 try state.abbrevs.abbrevs.ensureTotalCapacity(
275 bc.allocator,
276 @typeInfo(Abbrev.Builtin).Enum.fields.len + abbrevs.len,
277 );
278
279 assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.end_block));
280 try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{
281 .operands = &.{
282 .{ .literal = Abbrev.Builtin.end_block.toRecordId() },
283 .align_32_bits,
284 },
285 });
286 assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.enter_subblock));
287 try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{
288 .operands = &.{
289 .{ .literal = Abbrev.Builtin.enter_subblock.toRecordId() },
290 .{ .encoding = .{ .vbr = 8 } }, // blockid
291 .{ .encoding = .{ .vbr = 4 } }, // newabbrevlen
292 .align_32_bits,
293 .block_len,
294 },
295 });
296 assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.define_abbrev));
297 try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{
298 .operands = &.{
299 .{ .literal = Abbrev.Builtin.define_abbrev.toRecordId() },
300 .{ .encoding = .{ .array = 5 } }, // numabbrevops
301 .abbrev_op,
302 },
303 });
304 assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.unabbrev_record));
305 try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{
306 .operands = &.{
307 .{ .encoding = .{ .vbr = 6 } }, // code
308 .{ .encoding = .{ .array = 6 } }, // numops
309 .{ .encoding = .{ .vbr = 6 } }, // ops
310 },
311 });
312 assert(state.abbrevs.abbrevs.items.len == @typeInfo(Abbrev.Builtin).Enum.fields.len);
313 for (abbrevs) |abbrev| try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, abbrev);
314}
315
316fn endBlock(bc: *BitcodeReader) !void {
317 if (bc.stack.items.len == 0) return error.InvalidEndBlock;
318 bc.stack.items[bc.stack.items.len - 1].deinit(bc.allocator);
319 bc.stack.items.len -= 1;
320}
321
322fn parseBlockInfoBlock(bc: *BitcodeReader) !void {
323 var block_id: ?u32 = null;
324 while (true) {
325 const record = (try bc.nextRecord()) orelse return error.EndOfStream;
326 switch (record.id) {
327 Abbrev.Builtin.end_block.toRecordId() => break,
328 Abbrev.Builtin.define_abbrev.toRecordId() => {
329 const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse
330 return error.UnspecifiedBlockId);
331 if (!gop.found_existing) gop.value_ptr.* = Block.Info.default;
332 try gop.value_ptr.abbrevs.addOwnedAbbrev(
333 bc.allocator,
334 try record.toOwnedAbbrev(bc.allocator),
335 );
336 },
337 Block.Info.set_bid => block_id = std.math.cast(u32, record.operands[0]) orelse
338 return error.Overflow,
339 Block.Info.block_name => if (bc.keep_names) {
340 const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse
341 return error.UnspecifiedBlockId);
342 if (!gop.found_existing) gop.value_ptr.* = Block.Info.default;
343 const name = try bc.allocator.alloc(u8, record.operands.len);
344 errdefer bc.allocator.free(name);
345 for (name, record.operands) |*byte, operand|
346 byte.* = std.math.cast(u8, operand) orelse return error.InvalidName;
347 gop.value_ptr.block_name = name;
348 },
349 Block.Info.set_record_name => if (bc.keep_names) {
350 const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse
351 return error.UnspecifiedBlockId);
352 if (!gop.found_existing) gop.value_ptr.* = Block.Info.default;
353 const name = try bc.allocator.alloc(u8, record.operands.len - 1);
354 errdefer bc.allocator.free(name);
355 for (name, record.operands[1..]) |*byte, operand|
356 byte.* = std.math.cast(u8, operand) orelse return error.InvalidName;
357 try gop.value_ptr.record_names.put(
358 bc.allocator,
359 std.math.cast(u32, record.operands[0]) orelse return error.Overflow,
360 name,
361 );
362 },
363 else => return error.UnsupportedBlockInfoRecord,
364 }
365 }
366}
367
368fn align32Bits(bc: *BitcodeReader) void {
369 bc.bit_offset = 0;
370}
371
372fn read32Bits(bc: *BitcodeReader) !u32 {
373 assert(bc.bit_offset == 0);
374 return bc.reader.readInt(u32, .little);
375}
376
377fn readBytes(bc: *BitcodeReader, bytes: []u8) !void {
378 assert(bc.bit_offset == 0);
379 try bc.reader.readNoEof(bytes);
380
381 const trailing_bytes = bytes.len % 4;
382 if (trailing_bytes > 0) {
383 var bit_buffer = [1]u8{0} ** 4;
384 try bc.reader.readNoEof(bit_buffer[trailing_bytes..]);
385 bc.bit_buffer = std.mem.readInt(u32, &bit_buffer, .little);
386 bc.bit_offset = @intCast(trailing_bytes * 8);
387 }
388}
389
390fn readFixed(bc: *BitcodeReader, comptime T: type, bits: u7) !T {
391 var result: T = 0;
392 var shift: std.math.Log2IntCeil(T) = 0;
393 var remaining = bits;
394 while (remaining > 0) {
395 if (bc.bit_offset == 0) bc.bit_buffer = try bc.read32Bits();
396 const chunk_len = @min(@as(u6, 32) - bc.bit_offset, remaining);
397 const chunk_mask = @as(u32, std.math.maxInt(u32)) >> @intCast(32 - chunk_len);
398 result |= @as(T, @intCast(bc.bit_buffer >> bc.bit_offset & chunk_mask)) << @intCast(shift);
399 shift += @intCast(chunk_len);
400 remaining -= chunk_len;
401 bc.bit_offset = @truncate(bc.bit_offset + chunk_len);
402 }
403 return result;
404}
405
406fn readVbr(bc: *BitcodeReader, comptime T: type, bits: u7) !T {
407 const chunk_bits: u6 = @intCast(bits - 1);
408 const chunk_msb = @as(u64, 1) << chunk_bits;
409
410 var result: u64 = 0;
411 var shift: u6 = 0;
412 while (true) {
413 var chunk = try bc.readFixed(u64, bits);
414 result |= (chunk & (chunk_msb - 1)) << shift;
415 if (chunk & chunk_msb == 0) break;
416 shift += chunk_bits;
417 }
418 return @intCast(result);
419}
420
421fn readChar6(bc: *BitcodeReader) !u8 {
422 return switch (try bc.readFixed(u6, 6)) {
423 0...25 => |c| @as(u8, c - 0) + 'a',
424 26...51 => |c| @as(u8, c - 26) + 'A',
425 52...61 => |c| @as(u8, c - 52) + '0',
426 62 => '.',
427 63 => '_',
428 };
429}
430
431const State = struct {
432 block_id: ?u32,
433 abbrev_id_width: u6,
434 abbrevs: Abbrev.Store,
435
436 fn deinit(state: *State, allocator: std.mem.Allocator) void {
437 state.abbrevs.deinit(allocator);
438 state.* = undefined;
439 }
440};
441
442const Abbrev = struct {
443 operands: []const Operand,
444
445 const Builtin = enum(u2) {
446 end_block,
447 enter_subblock,
448 define_abbrev,
449 unabbrev_record,
450
451 const first_record_id: u32 = std.math.maxInt(u32) - @typeInfo(Builtin).Enum.fields.len + 1;
452 fn toRecordId(builtin: Builtin) u32 {
453 return first_record_id + @intFromEnum(builtin);
454 }
455 };
456
457 const Operand = union(enum) {
458 literal: u64,
459 encoding: union(Encoding) {
460 fixed: u7,
461 vbr: u6,
462 array: u3,
463 char6,
464 blob: u3,
465 },
466 align_32_bits,
467 block_len,
468 abbrev_op,
469
470 const literal = std.math.maxInt(u64);
471 const Encoding = enum(u3) {
472 fixed = 1,
473 vbr = 2,
474 array = 3,
475 char6 = 4,
476 blob = 5,
477 _,
478 };
479 };
480
481 const Store = struct {
482 abbrevs: std.ArrayListUnmanaged(Abbrev),
483
484 fn deinit(store: *Store, allocator: std.mem.Allocator) void {
485 for (store.abbrevs.items) |abbrev| allocator.free(abbrev.operands);
486 store.abbrevs.deinit(allocator);
487 store.* = undefined;
488 }
489
490 fn addAbbrev(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void {
491 try store.ensureUnusedCapacity(allocator, 1);
492 store.addAbbrevAssumeCapacity(abbrev);
493 }
494
495 fn addAbbrevAssumeCapacity(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void {
496 store.abbrevs.appendAssumeCapacity(.{
497 .operands = try allocator.dupe(Abbrev.Operand, abbrev.operands),
498 });
499 }
500
501 fn addOwnedAbbrev(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void {
502 try store.abbrevs.ensureUnusedCapacity(allocator, 1);
503 store.addOwnedAbbrevAssumeCapacity(abbrev);
504 }
505
506 fn addOwnedAbbrevAssumeCapacity(store: *Store, abbrev: Abbrev) void {
507 store.abbrevs.appendAssumeCapacity(abbrev);
508 }
509 };
510};
511
512const assert = std.debug.assert;
513const std = @import("std");
514
515const BitcodeReader = @This();
stage1/wasi.c+43-1
......@@ -178,6 +178,12 @@ struct wasi_ciovec {
178178 uint32_t len;
179179};
180180
181enum wasi_whence {
182 wasi_whence_set = 0,
183 wasi_whence_cur = 1,
184 wasi_whence_end = 2,
185};
186
181187extern uint8_t **const wasm_memory;
182188extern void wasm__start(void);
183189
......@@ -946,6 +952,43 @@ uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t io
946952 return wasi_errno_success;
947953}
948954
955uint32_t wasi_snapshot_preview1_fd_seek(uint32_t fd, uint64_t in_offset, uint32_t whence, uint32_t res_filesize) {
956 uint8_t *const m = *wasm_memory;
957 int64_t offset = (int64_t)in_offset;
958 uint64_t *res_filesize_ptr = (uint64_t *)&m[res_filesize];
959#if LOG_TRACE
960 fprintf(stderr, "wasi_snapshot_preview1_fd_seek(%u, 0x%lld, %u)\n", fd, (long long)offset, whence);
961#endif
962
963 if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf;
964 switch (des[fds[fd].de].filetype) {
965 case wasi_filetype_character_device: break;
966 case wasi_filetype_regular_file: break;
967 case wasi_filetype_directory: return wasi_errno_inval;
968 default: panic("unimplemented");
969 }
970
971 int seek_whence;
972 switch (whence) {
973 case wasi_whence_set:
974 seek_whence = SEEK_SET;
975 break;
976 case wasi_whence_cur:
977 seek_whence = SEEK_CUR;
978 break;
979 case wasi_whence_end:
980 seek_whence = SEEK_END;
981 break;
982 default:
983 return wasi_errno_inval;
984 }
985 if (fseek(fds[fd].stream, offset, seek_whence) < 0) return wasi_errno_io;
986 long res_offset = ftell(fds[fd].stream);
987 if (res_offset < 0) return wasi_errno_io;
988 *res_filesize_ptr = (uint64_t)res_offset;
989 return wasi_errno_success;
990}
991
949992uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t nsubscriptions, uint32_t res_nevents) {
950993 (void)in;
951994 (void)out;
......@@ -959,7 +1002,6 @@ uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t
9591002 return wasi_errno_success;
9601003}
9611004
962
9631005void wasi_snapshot_preview1_debug(uint32_t string, uint64_t x) {
9641006 uint8_t *const m = *wasm_memory;
9651007 const char *string_ptr = (const char *)&m[string];