mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge pull request #10924 from ziglang/air-independence-day
AIR independence day
This commit is contained in:
commit
2e1c16d649
@ -780,6 +780,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
pub fn allocatedSlice(self: Self) Slice {
|
||||
return self.items.ptr[0..self.capacity];
|
||||
}
|
||||
|
||||
/// Returns a slice of only the extra capacity after items.
|
||||
/// This can be useful for writing directly into an ArrayList.
|
||||
/// Note that such an operation must be followed up with a direct
|
||||
/// modification of `self.items.len`.
|
||||
pub fn unusedCapacitySlice(self: Self) Slice {
|
||||
return self.allocatedSlice()[self.items.len..];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
33
src/Air.zig
33
src/Air.zig
@ -32,8 +32,7 @@ pub const Inst = struct {
|
||||
/// The first N instructions in the main block must be one arg instruction per
|
||||
/// function parameter. This makes function parameters participate in
|
||||
/// liveness analysis without any special handling.
|
||||
/// Uses the `ty_str` field.
|
||||
/// The string is the parameter name.
|
||||
/// Uses the `ty` field.
|
||||
arg,
|
||||
/// Float or integer addition. For integers, wrapping is undefined behavior.
|
||||
/// Both operands are guaranteed to be the same type, and the result type
|
||||
@ -621,11 +620,6 @@ pub const Inst = struct {
|
||||
// Index into a different array.
|
||||
payload: u32,
|
||||
},
|
||||
ty_str: struct {
|
||||
ty: Ref,
|
||||
// ZIR string table index.
|
||||
str: u32,
|
||||
},
|
||||
br: struct {
|
||||
block_inst: Index,
|
||||
operand: Ref,
|
||||
@ -709,11 +703,25 @@ pub const Bin = struct {
|
||||
/// Trailing:
|
||||
/// 0. `Inst.Ref` for every outputs_len
|
||||
/// 1. `Inst.Ref` for every inputs_len
|
||||
/// 2. for every outputs_len
|
||||
/// - constraint: memory at this position is reinterpreted as a null
|
||||
/// terminated string. pad to the next u32 after the null byte.
|
||||
/// 3. for every inputs_len
|
||||
/// - constraint: memory at this position is reinterpreted as a null
|
||||
/// terminated string. pad to the next u32 after the null byte.
|
||||
/// 4. for every clobbers_len
|
||||
/// - clobber_name: memory at this position is reinterpreted as a null
|
||||
/// terminated string. pad to the next u32 after the null byte.
|
||||
/// 5. A number of u32 elements follow according to the equation `(source_len + 3) / 4`.
|
||||
/// Memory starting at this position is reinterpreted as the source bytes.
|
||||
pub const Asm = struct {
|
||||
/// Index to the corresponding ZIR instruction.
|
||||
/// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and
|
||||
/// clobbers are found via here.
|
||||
zir_index: u32,
|
||||
/// Length of the assembly source in bytes.
|
||||
source_len: u32,
|
||||
outputs_len: u32,
|
||||
inputs_len: u32,
|
||||
/// The MSB is `is_volatile`.
|
||||
/// The rest of the bits are `clobbers_len`.
|
||||
flags: u32,
|
||||
};
|
||||
|
||||
pub const Cmpxchg = struct {
|
||||
@ -765,8 +773,6 @@ pub fn typeOf(air: Air, inst: Air.Inst.Ref) Type {
|
||||
pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
const datas = air.instructions.items(.data);
|
||||
switch (air.instructions.items(.tag)[inst]) {
|
||||
.arg => return air.getRefType(datas[inst].ty_str.ty),
|
||||
|
||||
.add,
|
||||
.addwrap,
|
||||
.add_sat,
|
||||
@ -833,6 +839,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
|
||||
.alloc,
|
||||
.ret_ptr,
|
||||
.arg,
|
||||
=> return datas[inst].ty,
|
||||
|
||||
.assembly,
|
||||
|
||||
@ -2778,7 +2778,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress
|
||||
errdefer if (!liveness_frame_ended) liveness_frame.end();
|
||||
|
||||
log.debug("analyze liveness of {s}", .{decl.name});
|
||||
var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir);
|
||||
var liveness = try Liveness.analyze(gpa, air);
|
||||
defer liveness.deinit(gpa);
|
||||
|
||||
liveness_frame.end();
|
||||
@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress
|
||||
|
||||
if (builtin.mode == .Debug and comp.verbose_air) {
|
||||
std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
|
||||
@import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness);
|
||||
@import("print_air.zig").dump(gpa, air, liveness);
|
||||
std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness);
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Air = @import("Air.zig");
|
||||
const Zir = @import("Zir.zig");
|
||||
const Log2Int = std.math.Log2Int;
|
||||
|
||||
/// This array is split into sets of 4 bits per AIR instruction.
|
||||
@ -52,7 +51,7 @@ pub const SwitchBr = struct {
|
||||
else_death_count: u32,
|
||||
};
|
||||
|
||||
pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {
|
||||
pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {
|
||||
),
|
||||
.extra = .{},
|
||||
.special = .{},
|
||||
.zir = &zir,
|
||||
};
|
||||
errdefer gpa.free(a.tomb_bits);
|
||||
errdefer a.special.deinit(gpa);
|
||||
@ -157,7 +155,6 @@ const Analysis = struct {
|
||||
tomb_bits: []usize,
|
||||
special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
|
||||
extra: std.ArrayListUnmanaged(u32),
|
||||
zir: *const Zir,
|
||||
|
||||
fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void {
|
||||
const usize_index = (inst * bpi) / @bitSizeOf(usize);
|
||||
@ -446,15 +443,24 @@ fn analyzeInst(
|
||||
},
|
||||
.assembly => {
|
||||
const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload);
|
||||
const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended;
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const inputs_len = @truncate(u5, extended.small >> 5);
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]);
|
||||
const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]);
|
||||
if (outputs.len + args.len <= bpi - 1) {
|
||||
var extra_i: usize = extra.end;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]);
|
||||
extra_i += outputs.len;
|
||||
const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]);
|
||||
extra_i += inputs.len;
|
||||
|
||||
simple: {
|
||||
var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
|
||||
std.mem.copy(Air.Inst.Ref, &buf, outputs);
|
||||
std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
|
||||
var buf_index: usize = 0;
|
||||
for (outputs) |output| {
|
||||
if (output != .none) {
|
||||
if (buf_index >= buf.len) break :simple;
|
||||
buf[buf_index] = output;
|
||||
buf_index += 1;
|
||||
}
|
||||
}
|
||||
if (buf_index + inputs.len > buf.len) break :simple;
|
||||
std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
|
||||
return trackOperands(a, new_set, inst, main_tomb, buf);
|
||||
}
|
||||
var extra_tombs: ExtraTombs = .{
|
||||
@ -464,10 +470,12 @@ fn analyzeInst(
|
||||
.main_tomb = main_tomb,
|
||||
};
|
||||
for (outputs) |output| {
|
||||
try extra_tombs.feed(output);
|
||||
if (output != .none) {
|
||||
try extra_tombs.feed(output);
|
||||
}
|
||||
}
|
||||
for (args) |arg| {
|
||||
try extra_tombs.feed(arg);
|
||||
for (inputs) |input| {
|
||||
try extra_tombs.feed(input);
|
||||
}
|
||||
return extra_tombs.finish();
|
||||
},
|
||||
|
||||
@ -1370,6 +1370,14 @@ pub const Fn = struct {
|
||||
/// ZIR instruction.
|
||||
zir_body_inst: Zir.Inst.Index,
|
||||
|
||||
/// Prefer to use `getParamName` to access this because of the future improvement
|
||||
/// we want to do mentioned in the TODO below.
|
||||
/// Stored in gpa.
|
||||
/// TODO: change param ZIR instructions to be embedded inside the function
|
||||
/// ZIR instruction instead of before it, so that `zir_body_inst` can be used to
|
||||
/// determine param names rather than redundantly storing them here.
|
||||
param_names: []const [:0]const u8,
|
||||
|
||||
/// Relative to owner Decl.
|
||||
lbrace_line: u32,
|
||||
/// Relative to owner Decl.
|
||||
@ -1466,6 +1474,18 @@ pub const Fn = struct {
|
||||
gpa.destroy(node);
|
||||
it = next;
|
||||
}
|
||||
|
||||
for (func.param_names) |param_name| {
|
||||
gpa.free(param_name);
|
||||
}
|
||||
gpa.free(func.param_names);
|
||||
}
|
||||
|
||||
pub fn getParamName(func: Fn, index: u32) [:0]const u8 {
|
||||
// TODO rework ZIR of parameters so that this function looks up
|
||||
// param names in ZIR instead of redundantly saving them into Fn.
|
||||
// const zir = func.owner_decl.getFileScope().zir;
|
||||
return func.param_names[index];
|
||||
}
|
||||
};
|
||||
|
||||
@ -4606,15 +4626,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
|
||||
runtime_param_index += 1;
|
||||
continue;
|
||||
}
|
||||
const ty_ref = try sema.addType(param_type);
|
||||
const arg_index = @intCast(u32, sema.air_instructions.len);
|
||||
inner_block.instructions.appendAssumeCapacity(arg_index);
|
||||
sema.air_instructions.appendAssumeCapacity(.{
|
||||
.tag = .arg,
|
||||
.data = .{ .ty_str = .{
|
||||
.ty = ty_ref,
|
||||
.str = param.name,
|
||||
} },
|
||||
.data = .{ .ty = param_type },
|
||||
});
|
||||
sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
|
||||
total_param_index += 1;
|
||||
|
||||
80
src/Sema.zig
80
src/Sema.zig
@ -134,6 +134,7 @@ pub const Block = struct {
|
||||
/// `noreturn` means `anytype`.
|
||||
ty: Type,
|
||||
is_comptime: bool,
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
/// This `Block` maps a block ZIR instruction to the corresponding
|
||||
@ -284,13 +285,10 @@ pub const Block = struct {
|
||||
});
|
||||
}
|
||||
|
||||
fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref {
|
||||
fn addArg(block: *Block, ty: Type) error{OutOfMemory}!Air.Inst.Ref {
|
||||
return block.addInst(.{
|
||||
.tag = .arg,
|
||||
.data = .{ .ty_str = .{
|
||||
.ty = try block.sema.addType(ty),
|
||||
.str = name,
|
||||
} },
|
||||
.data = .{ .ty = ty },
|
||||
});
|
||||
}
|
||||
|
||||
@ -1126,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
.frame_address => return sema.zirFrameAddress( block, extended),
|
||||
.alloc => return sema.zirAllocExtended( block, extended),
|
||||
.builtin_extern => return sema.zirBuiltinExtern( block, extended),
|
||||
.@"asm" => return sema.zirAsm( block, extended, inst),
|
||||
.@"asm" => return sema.zirAsm( block, extended),
|
||||
.typeof_peer => return sema.zirTypeofPeer( block, extended),
|
||||
.compile_log => return sema.zirCompileLog( block, extended),
|
||||
.add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
|
||||
@ -4645,7 +4643,7 @@ fn analyzeCall(
|
||||
} else {
|
||||
// We insert into the map an instruction which is runtime-known
|
||||
// but has the type of the argument.
|
||||
const child_arg = try child_block.addArg(arg_ty, 0);
|
||||
const child_arg = try child_block.addArg(arg_ty);
|
||||
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
|
||||
}
|
||||
}
|
||||
@ -5712,6 +5710,11 @@ fn funcCommon(
|
||||
break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;
|
||||
} else null;
|
||||
|
||||
const param_names = try sema.gpa.alloc([:0]const u8, block.params.items.len);
|
||||
for (param_names) |*param_name, i| {
|
||||
param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name);
|
||||
}
|
||||
|
||||
const fn_payload = try sema.arena.create(Value.Payload.Function);
|
||||
new_func.* = .{
|
||||
.state = anal_state,
|
||||
@ -5722,6 +5725,7 @@ fn funcCommon(
|
||||
.rbrace_line = src_locs.rbrace_line,
|
||||
.lbrace_column = @truncate(u16, src_locs.columns),
|
||||
.rbrace_column = @truncate(u16, src_locs.columns >> 16),
|
||||
.param_names = param_names,
|
||||
};
|
||||
if (maybe_inferred_error_set_node) |node| {
|
||||
new_func.inferred_error_sets.prepend(node);
|
||||
@ -5746,10 +5750,6 @@ fn zirParam(
|
||||
const param_name = sema.code.nullTerminatedString(extra.data.name);
|
||||
const body = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
|
||||
// TODO check if param_name shadows a Decl. This only needs to be done if
|
||||
// usingnamespace is implemented.
|
||||
_ = param_name;
|
||||
|
||||
// We could be in a generic function instantiation, or we could be evaluating a generic
|
||||
// function without any comptime args provided.
|
||||
const param_ty = param_ty: {
|
||||
@ -5776,6 +5776,7 @@ fn zirParam(
|
||||
try block.params.append(sema.gpa, .{
|
||||
.ty = Type.initTag(.generic_poison),
|
||||
.is_comptime = comptime_syntax,
|
||||
.name = param_name,
|
||||
});
|
||||
try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison);
|
||||
return;
|
||||
@ -5801,6 +5802,7 @@ fn zirParam(
|
||||
try block.params.append(sema.gpa, .{
|
||||
.ty = param_ty,
|
||||
.is_comptime = is_comptime,
|
||||
.name = param_name,
|
||||
});
|
||||
const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
|
||||
try sema.inst_map.putNoClobber(sema.gpa, inst, result);
|
||||
@ -5816,10 +5818,6 @@ fn zirParamAnytype(
|
||||
const src = inst_data.src();
|
||||
const param_name = inst_data.get(sema.code);
|
||||
|
||||
// TODO check if param_name shadows a Decl. This only needs to be done if
|
||||
// usingnamespace is implemented.
|
||||
_ = param_name;
|
||||
|
||||
if (sema.inst_map.get(inst)) |air_ref| {
|
||||
const param_ty = sema.typeOf(air_ref);
|
||||
if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) {
|
||||
@ -5831,6 +5829,7 @@ fn zirParamAnytype(
|
||||
try block.params.append(sema.gpa, .{
|
||||
.ty = param_ty,
|
||||
.is_comptime = false,
|
||||
.name = param_name,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -5840,6 +5839,7 @@ fn zirParamAnytype(
|
||||
try block.params.append(sema.gpa, .{
|
||||
.ty = Type.initTag(.generic_poison),
|
||||
.is_comptime = comptime_syntax,
|
||||
.name = param_name,
|
||||
});
|
||||
try sema.inst_map.put(sema.gpa, inst, .generic_poison);
|
||||
}
|
||||
@ -9083,7 +9083,6 @@ fn zirAsm(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
extended: Zir.Inst.Extended.InstData,
|
||||
inst: Zir.Inst.Index,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@ -9094,6 +9093,7 @@ fn zirAsm(
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const inputs_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||
|
||||
if (extra.data.asm_source == 0) {
|
||||
// This can move to become an AstGen error after inline assembly improvements land
|
||||
@ -9107,6 +9107,7 @@ fn zirAsm(
|
||||
|
||||
var extra_i = extra.end;
|
||||
var output_type_bits = extra.data.output_type_bits;
|
||||
var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len;
|
||||
|
||||
const Output = struct { constraint: []const u8, ty: Type };
|
||||
const output: ?Output = if (outputs_len == 0) null else blk: {
|
||||
@ -9121,6 +9122,8 @@ fn zirAsm(
|
||||
}
|
||||
|
||||
const constraint = sema.code.nullTerminatedString(output.data.constraint);
|
||||
needed_capacity += constraint.len / 4 + 1;
|
||||
|
||||
break :blk Output{
|
||||
.constraint = constraint,
|
||||
.ty = try sema.resolveType(block, ret_ty_src, output.data.operand),
|
||||
@ -9138,28 +9141,65 @@ fn zirAsm(
|
||||
_ = name; // TODO: use the name
|
||||
|
||||
arg.* = sema.resolveInst(input.data.operand);
|
||||
inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint);
|
||||
const constraint = sema.code.nullTerminatedString(input.data.constraint);
|
||||
needed_capacity += constraint.len / 4 + 1;
|
||||
inputs[arg_i] = constraint;
|
||||
}
|
||||
|
||||
const clobbers = try sema.arena.alloc([]const u8, clobbers_len);
|
||||
for (clobbers) |*name| {
|
||||
name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
|
||||
extra_i += 1;
|
||||
|
||||
needed_capacity += name.*.len / 4 + 1;
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
const asm_source = sema.code.nullTerminatedString(extra.data.asm_source);
|
||||
needed_capacity += (asm_source.len + 3) / 4;
|
||||
|
||||
const gpa = sema.gpa;
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Asm).Struct.fields.len + args.len);
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
|
||||
const asm_air = try block.addInst(.{
|
||||
.tag = .assembly,
|
||||
.data = .{ .ty_pl = .{
|
||||
.ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type,
|
||||
.payload = sema.addExtraAssumeCapacity(Air.Asm{
|
||||
.zir_index = inst,
|
||||
.source_len = @intCast(u32, asm_source.len),
|
||||
.outputs_len = outputs_len,
|
||||
.inputs_len = @intCast(u32, args.len),
|
||||
.flags = (@as(u32, @boolToInt(is_volatile)) << 31) | @intCast(u32, clobbers.len),
|
||||
}),
|
||||
} },
|
||||
});
|
||||
if (output != null) {
|
||||
// Indicate the output is the asm instruction return value.
|
||||
sema.air_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.none));
|
||||
}
|
||||
sema.appendRefsAssumeCapacity(args);
|
||||
if (output) |o| {
|
||||
const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
|
||||
mem.copy(u8, buffer, o.constraint);
|
||||
buffer[o.constraint.len] = 0;
|
||||
sema.air_extra.items.len += o.constraint.len / 4 + 1;
|
||||
}
|
||||
for (inputs) |constraint| {
|
||||
const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
|
||||
mem.copy(u8, buffer, constraint);
|
||||
buffer[constraint.len] = 0;
|
||||
sema.air_extra.items.len += constraint.len / 4 + 1;
|
||||
}
|
||||
for (clobbers) |clobber| {
|
||||
const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
|
||||
mem.copy(u8, buffer, clobber);
|
||||
buffer[clobber.len] = 0;
|
||||
sema.air_extra.items.len += clobber.len / 4 + 1;
|
||||
}
|
||||
{
|
||||
const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
|
||||
mem.copy(u8, buffer, asm_source);
|
||||
sema.air_extra.items.len += (asm_source.len + 3) / 4;
|
||||
}
|
||||
return asm_air;
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ const mem = std.mem;
|
||||
const math = std.math;
|
||||
const assert = std.debug.assert;
|
||||
const Air = @import("../../Air.zig");
|
||||
const Zir = @import("../../Zir.zig");
|
||||
const Mir = @import("Mir.zig");
|
||||
const Emit = @import("Emit.zig");
|
||||
const Liveness = @import("../../Liveness.zig");
|
||||
@ -2021,36 +2020,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
//return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
|
||||
}
|
||||
|
||||
fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
|
||||
const ty_str = self.air.instructions.items(.data)[inst].ty_str;
|
||||
const zir = &self.mod_fn.owner_decl.getFileScope().zir;
|
||||
const name = zir.nullTerminatedString(ty_str.str);
|
||||
const name_with_null = name.ptr[0 .. name.len + 1];
|
||||
const ty = self.air.getRefType(ty_str.ty);
|
||||
|
||||
switch (mcv) {
|
||||
.register => |reg| {
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
try dbg_out.dbg_info.ensureUnusedCapacity(3);
|
||||
dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
|
||||
dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
|
||||
1, // ULEB128 dwarf expression length
|
||||
reg.dwarfLocOp(),
|
||||
});
|
||||
try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
|
||||
try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
|
||||
dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
},
|
||||
.stack_offset => {},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn airArg(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const arg_index = self.arg_index;
|
||||
self.arg_index += 1;
|
||||
@ -2866,40 +2835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
|
||||
}
|
||||
|
||||
fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const air_datas = self.air.instructions.items(.data);
|
||||
const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
|
||||
const zir = self.mod_fn.owner_decl.getFileScope().zir;
|
||||
const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
|
||||
const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
|
||||
const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const args_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
_ = clobbers_len; // TODO honor these
|
||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
|
||||
const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
|
||||
|
||||
if (outputs_len > 1) {
|
||||
return self.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
var extra_i: usize = zir_extra.end;
|
||||
const output_constraint: ?[]const u8 = out: {
|
||||
var i: usize = 0;
|
||||
while (i < outputs_len) : (i += 1) {
|
||||
const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
|
||||
extra_i = output.end;
|
||||
break :out zir.nullTerminatedString(output.data.constraint);
|
||||
}
|
||||
break :out null;
|
||||
};
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
|
||||
const clobbers_len = @truncate(u31, extra.data.flags);
|
||||
var extra_i: usize = extra.end;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
|
||||
extra_i += outputs.len;
|
||||
const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
|
||||
extra_i += inputs.len;
|
||||
|
||||
const dead = !is_volatile and self.liveness.isUnused(inst);
|
||||
const result: MCValue = if (dead) .dead else result: {
|
||||
for (args) |arg| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
if (outputs.len > 1) {
|
||||
return self.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
|
||||
const output_constraint: ?[]const u8 = for (outputs) |output| {
|
||||
if (output != .none) {
|
||||
return self.fail("TODO implement codegen for non-expr asm", .{});
|
||||
}
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
break constraint;
|
||||
} else null;
|
||||
|
||||
for (inputs) |input| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
|
||||
return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
|
||||
@ -2908,11 +2876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail("unrecognized register: '{s}'", .{reg_name});
|
||||
|
||||
const arg_mcv = try self.resolveInst(arg);
|
||||
const arg_mcv = try self.resolveInst(input);
|
||||
try self.register_manager.getReg(reg, null);
|
||||
try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
|
||||
try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
|
||||
}
|
||||
|
||||
{
|
||||
var clobber_i: u32 = 0;
|
||||
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
||||
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += clobber.len / 4 + 1;
|
||||
|
||||
// TODO honor these
|
||||
}
|
||||
}
|
||||
|
||||
const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
|
||||
if (mem.eql(u8, asm_source, "svc #0")) {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .svc,
|
||||
@ -2939,18 +2921,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
break :result MCValue{ .none = {} };
|
||||
}
|
||||
};
|
||||
if (outputs.len + args.len <= Liveness.bpi - 1) {
|
||||
|
||||
simple: {
|
||||
var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
|
||||
std.mem.copy(Air.Inst.Ref, &buf, outputs);
|
||||
std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
|
||||
var buf_index: usize = 0;
|
||||
for (outputs) |output| {
|
||||
if (output == .none) continue;
|
||||
|
||||
if (buf_index >= buf.len) break :simple;
|
||||
buf[buf_index] = output;
|
||||
buf_index += 1;
|
||||
}
|
||||
if (buf_index + inputs.len > buf.len) break :simple;
|
||||
std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
|
||||
return self.finishAir(inst, result, buf);
|
||||
}
|
||||
var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
|
||||
var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
|
||||
for (outputs) |output| {
|
||||
if (output == .none) continue;
|
||||
|
||||
bt.feed(output);
|
||||
}
|
||||
for (args) |arg| {
|
||||
bt.feed(arg);
|
||||
for (inputs) |input| {
|
||||
bt.feed(input);
|
||||
}
|
||||
return bt.finishAir(result);
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ const mem = std.mem;
|
||||
const math = std.math;
|
||||
const assert = std.debug.assert;
|
||||
const Air = @import("../../Air.zig");
|
||||
const Zir = @import("../../Zir.zig");
|
||||
const Mir = @import("Mir.zig");
|
||||
const Emit = @import("Emit.zig");
|
||||
const Liveness = @import("../../Liveness.zig");
|
||||
@ -3075,40 +3074,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
|
||||
}
|
||||
|
||||
fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const air_datas = self.air.instructions.items(.data);
|
||||
const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
|
||||
const zir = self.mod_fn.owner_decl.getFileScope().zir;
|
||||
const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
|
||||
const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
|
||||
const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const args_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
_ = clobbers_len; // TODO honor these
|
||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
|
||||
const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
|
||||
|
||||
if (outputs_len > 1) {
|
||||
return self.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
var extra_i: usize = zir_extra.end;
|
||||
const output_constraint: ?[]const u8 = out: {
|
||||
var i: usize = 0;
|
||||
while (i < outputs_len) : (i += 1) {
|
||||
const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
|
||||
extra_i = output.end;
|
||||
break :out zir.nullTerminatedString(output.data.constraint);
|
||||
}
|
||||
break :out null;
|
||||
};
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
|
||||
const clobbers_len = @truncate(u31, extra.data.flags);
|
||||
var extra_i: usize = extra.end;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
|
||||
extra_i += outputs.len;
|
||||
const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
|
||||
extra_i += inputs.len;
|
||||
|
||||
const dead = !is_volatile and self.liveness.isUnused(inst);
|
||||
const result: MCValue = if (dead) .dead else result: {
|
||||
for (args) |arg| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
if (outputs.len > 1) {
|
||||
return self.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
|
||||
const output_constraint: ?[]const u8 = for (outputs) |output| {
|
||||
if (output != .none) {
|
||||
return self.fail("TODO implement codegen for non-expr asm", .{});
|
||||
}
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
break constraint;
|
||||
} else null;
|
||||
|
||||
for (inputs) |input| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
|
||||
return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
|
||||
@ -3117,11 +3115,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail("unrecognized register: '{s}'", .{reg_name});
|
||||
|
||||
const arg_mcv = try self.resolveInst(arg);
|
||||
const arg_mcv = try self.resolveInst(input);
|
||||
try self.register_manager.getReg(reg, null);
|
||||
try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
|
||||
try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
|
||||
}
|
||||
|
||||
{
|
||||
var clobber_i: u32 = 0;
|
||||
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
||||
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += clobber.len / 4 + 1;
|
||||
|
||||
// TODO honor these
|
||||
}
|
||||
}
|
||||
|
||||
const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
|
||||
if (mem.eql(u8, asm_source, "svc #0")) {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .svc,
|
||||
@ -3144,18 +3156,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
break :result MCValue{ .none = {} };
|
||||
}
|
||||
};
|
||||
if (outputs.len + args.len <= Liveness.bpi - 1) {
|
||||
|
||||
simple: {
|
||||
var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
|
||||
std.mem.copy(Air.Inst.Ref, &buf, outputs);
|
||||
std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
|
||||
var buf_index: usize = 0;
|
||||
for (outputs) |output| {
|
||||
if (output == .none) continue;
|
||||
|
||||
if (buf_index >= buf.len) break :simple;
|
||||
buf[buf_index] = output;
|
||||
buf_index += 1;
|
||||
}
|
||||
if (buf_index + inputs.len > buf.len) break :simple;
|
||||
std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
|
||||
return self.finishAir(inst, result, buf);
|
||||
}
|
||||
var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
|
||||
var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
|
||||
for (outputs) |output| {
|
||||
if (output == .none) continue;
|
||||
|
||||
bt.feed(output);
|
||||
}
|
||||
for (args) |arg| {
|
||||
bt.feed(arg);
|
||||
for (inputs) |input| {
|
||||
bt.feed(input);
|
||||
}
|
||||
return bt.finishAir(result);
|
||||
}
|
||||
|
||||
@ -393,11 +393,9 @@ fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void {
|
||||
fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void {
|
||||
const mcv = self.function.args[arg_index];
|
||||
|
||||
const ty_str = self.function.air.instructions.items(.data)[inst].ty_str;
|
||||
const zir = &self.function.mod_fn.owner_decl.getFileScope().zir;
|
||||
const name = zir.nullTerminatedString(ty_str.str);
|
||||
const ty = self.function.air.instructions.items(.data)[inst].ty;
|
||||
const name = self.function.mod_fn.getParamName(arg_index);
|
||||
const name_with_null = name.ptr[0 .. name.len + 1];
|
||||
const ty = self.function.air.getRefType(ty_str.ty);
|
||||
|
||||
switch (mcv) {
|
||||
.register => |reg| {
|
||||
|
||||
@ -4,7 +4,6 @@ const mem = std.mem;
|
||||
const math = std.math;
|
||||
const assert = std.debug.assert;
|
||||
const Air = @import("../../Air.zig");
|
||||
const Zir = @import("../../Zir.zig");
|
||||
const Mir = @import("Mir.zig");
|
||||
const Emit = @import("Emit.zig");
|
||||
const Liveness = @import("../../Liveness.zig");
|
||||
@ -1354,12 +1353,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
//return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
|
||||
}
|
||||
|
||||
fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
|
||||
const ty_str = self.air.instructions.items(.data)[inst].ty_str;
|
||||
const zir = &self.mod_fn.owner_decl.getFileScope().zir;
|
||||
const name = zir.nullTerminatedString(ty_str.str);
|
||||
fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void {
|
||||
const ty = self.air.instructions.items(.data)[inst].ty;
|
||||
const name = self.mod_fn.getParamName(arg_index);
|
||||
const name_with_null = name.ptr[0 .. name.len + 1];
|
||||
const ty = self.air.getRefType(ty_str.ty);
|
||||
|
||||
switch (mcv) {
|
||||
.register => |reg| {
|
||||
@ -1402,7 +1399,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
|
||||
// TODO support stack-only arguments
|
||||
// TODO Copy registers to the stack
|
||||
const mcv = result;
|
||||
try self.genArgDbgInfo(inst, mcv);
|
||||
try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index));
|
||||
|
||||
if (self.liveness.isUnused(inst))
|
||||
return self.finishAirBookkeeping();
|
||||
@ -1838,40 +1835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
|
||||
}
|
||||
|
||||
fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const air_datas = self.air.instructions.items(.data);
|
||||
const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
|
||||
const zir = self.mod_fn.owner_decl.getFileScope().zir;
|
||||
const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
|
||||
const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
|
||||
const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const args_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
_ = clobbers_len; // TODO honor these
|
||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
|
||||
const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
|
||||
|
||||
if (outputs_len > 1) {
|
||||
return self.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
var extra_i: usize = zir_extra.end;
|
||||
const output_constraint: ?[]const u8 = out: {
|
||||
var i: usize = 0;
|
||||
while (i < outputs_len) : (i += 1) {
|
||||
const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
|
||||
extra_i = output.end;
|
||||
break :out zir.nullTerminatedString(output.data.constraint);
|
||||
}
|
||||
break :out null;
|
||||
};
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
|
||||
const clobbers_len = @truncate(u31, extra.data.flags);
|
||||
var extra_i: usize = extra.end;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
|
||||
extra_i += outputs.len;
|
||||
const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
|
||||
extra_i += inputs.len;
|
||||
|
||||
const dead = !is_volatile and self.liveness.isUnused(inst);
|
||||
const result: MCValue = if (dead) .dead else result: {
|
||||
for (args) |arg| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
if (outputs.len > 1) {
|
||||
return self.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
|
||||
const output_constraint: ?[]const u8 = for (outputs) |output| {
|
||||
if (output != .none) {
|
||||
return self.fail("TODO implement codegen for non-expr asm", .{});
|
||||
}
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
break constraint;
|
||||
} else null;
|
||||
|
||||
for (inputs) |input| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
|
||||
return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
|
||||
@ -1880,11 +1876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail("unrecognized register: '{s}'", .{reg_name});
|
||||
|
||||
const arg_mcv = try self.resolveInst(arg);
|
||||
const arg_mcv = try self.resolveInst(input);
|
||||
try self.register_manager.getReg(reg, null);
|
||||
try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
|
||||
try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
|
||||
}
|
||||
|
||||
{
|
||||
var clobber_i: u32 = 0;
|
||||
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
||||
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += clobber.len / 4 + 1;
|
||||
|
||||
// TODO honor these
|
||||
}
|
||||
}
|
||||
|
||||
const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
|
||||
if (mem.eql(u8, asm_source, "ecall")) {
|
||||
_ = try self.addInst(.{
|
||||
.tag = .ecall,
|
||||
@ -1906,18 +1916,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
break :result MCValue{ .none = {} };
|
||||
}
|
||||
};
|
||||
if (outputs.len + args.len <= Liveness.bpi - 1) {
|
||||
simple: {
|
||||
var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
|
||||
std.mem.copy(Air.Inst.Ref, &buf, outputs);
|
||||
std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
|
||||
var buf_index: usize = 0;
|
||||
for (outputs) |output| {
|
||||
if (output == .none) continue;
|
||||
|
||||
if (buf_index >= buf.len) break :simple;
|
||||
buf[buf_index] = output;
|
||||
buf_index += 1;
|
||||
}
|
||||
if (buf_index + inputs.len > buf.len) break :simple;
|
||||
std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
|
||||
return self.finishAir(inst, result, buf);
|
||||
}
|
||||
var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
|
||||
var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
|
||||
for (outputs) |output| {
|
||||
if (output == .none) continue;
|
||||
|
||||
bt.feed(output);
|
||||
}
|
||||
for (args) |arg| {
|
||||
bt.feed(arg);
|
||||
for (inputs) |input| {
|
||||
bt.feed(input);
|
||||
}
|
||||
return bt.finishAir(result);
|
||||
}
|
||||
|
||||
@ -26,7 +26,6 @@ const Target = std.Target;
|
||||
const Type = @import("../../type.zig").Type;
|
||||
const TypedValue = @import("../../TypedValue.zig");
|
||||
const Value = @import("../../value.zig").Value;
|
||||
const Zir = @import("../../Zir.zig");
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
@ -3435,41 +3434,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
|
||||
}
|
||||
|
||||
fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const air_datas = self.air.instructions.items(.data);
|
||||
const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
|
||||
const zir = self.mod_fn.owner_decl.getFileScope().zir;
|
||||
const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
|
||||
const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
|
||||
const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const args_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
_ = clobbers_len; // TODO honor these
|
||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||
const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..args_len]);
|
||||
|
||||
if (outputs_len > 1) {
|
||||
return self.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
var extra_i: usize = zir_extra.end;
|
||||
const output_constraint: ?[]const u8 = out: {
|
||||
var i: usize = 0;
|
||||
while (i < outputs_len) : (i += 1) {
|
||||
const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
|
||||
extra_i = output.end;
|
||||
break :out zir.nullTerminatedString(output.data.constraint);
|
||||
}
|
||||
break :out null;
|
||||
};
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
|
||||
const clobbers_len = @truncate(u31, extra.data.flags);
|
||||
var extra_i: usize = extra.end;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
|
||||
extra_i += outputs.len;
|
||||
const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
|
||||
extra_i += inputs.len;
|
||||
|
||||
const dead = !is_volatile and self.liveness.isUnused(inst);
|
||||
const result: MCValue = if (dead)
|
||||
.dead
|
||||
else result: {
|
||||
for (args) |arg| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
const result: MCValue = if (dead) .dead else result: {
|
||||
if (outputs.len > 1) {
|
||||
return self.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
|
||||
const output_constraint: ?[]const u8 = for (outputs) |output| {
|
||||
if (output != .none) {
|
||||
return self.fail("TODO implement codegen for non-expr asm", .{});
|
||||
}
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
break constraint;
|
||||
} else null;
|
||||
|
||||
for (inputs) |input| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
|
||||
return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
|
||||
@ -3478,11 +3475,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const reg = parseRegName(reg_name) orelse
|
||||
return self.fail("unrecognized register: '{s}'", .{reg_name});
|
||||
|
||||
const arg_mcv = try self.resolveInst(arg);
|
||||
const arg_mcv = try self.resolveInst(input);
|
||||
try self.register_manager.getReg(reg, null);
|
||||
try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
|
||||
try self.genSetReg(self.air.typeOf(input), reg, arg_mcv);
|
||||
}
|
||||
|
||||
{
|
||||
var clobber_i: u32 = 0;
|
||||
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
||||
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += clobber.len / 4 + 1;
|
||||
|
||||
// TODO honor these
|
||||
}
|
||||
}
|
||||
|
||||
const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
|
||||
{
|
||||
var iter = std.mem.tokenize(u8, asm_source, "\n\r");
|
||||
while (iter.next()) |ins| {
|
||||
@ -3549,14 +3560,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
|
||||
break :result MCValue{ .none = {} };
|
||||
}
|
||||
};
|
||||
if (args.len <= Liveness.bpi - 1) {
|
||||
|
||||
simple: {
|
||||
var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
|
||||
std.mem.copy(Air.Inst.Ref, &buf, args);
|
||||
var buf_index: usize = 0;
|
||||
for (outputs) |output| {
|
||||
if (output == .none) continue;
|
||||
|
||||
if (buf_index >= buf.len) break :simple;
|
||||
buf[buf_index] = output;
|
||||
buf_index += 1;
|
||||
}
|
||||
if (buf_index + inputs.len > buf.len) break :simple;
|
||||
std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs);
|
||||
return self.finishAir(inst, result, buf);
|
||||
}
|
||||
var bt = try self.iterateBigTomb(inst, args.len);
|
||||
for (args) |arg| {
|
||||
bt.feed(arg);
|
||||
var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
|
||||
for (outputs) |output| {
|
||||
if (output == .none) continue;
|
||||
|
||||
bt.feed(output);
|
||||
}
|
||||
for (inputs) |input| {
|
||||
bt.feed(input);
|
||||
}
|
||||
return bt.finishAir(result);
|
||||
}
|
||||
@ -3635,7 +3661,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
|
||||
const reg = try self.copyToTmpRegister(ty, mcv);
|
||||
return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
|
||||
},
|
||||
else => return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}),
|
||||
else => return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}),
|
||||
}
|
||||
},
|
||||
.embedded_in_code => {
|
||||
@ -3643,7 +3669,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
|
||||
const reg = try self.copyToTmpRegister(ty, mcv);
|
||||
return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
|
||||
}
|
||||
return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv});
|
||||
return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv});
|
||||
},
|
||||
.memory,
|
||||
.direct_load,
|
||||
|
||||
@ -946,15 +946,13 @@ fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
|
||||
const payload = emit.mir.instructions.items(.data)[inst].payload;
|
||||
const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data;
|
||||
const mcv = emit.mir.function.args[arg_dbg_info.arg_index];
|
||||
try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack);
|
||||
try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack, arg_dbg_info.arg_index);
|
||||
}
|
||||
|
||||
fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32) !void {
|
||||
const ty_str = emit.mir.function.air.instructions.items(.data)[inst].ty_str;
|
||||
const zir = &emit.mir.function.mod_fn.owner_decl.getFileScope().zir;
|
||||
const name = zir.nullTerminatedString(ty_str.str);
|
||||
fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32, arg_index: u32) !void {
|
||||
const ty = emit.mir.function.air.instructions.items(.data)[inst].ty;
|
||||
const name = emit.mir.function.mod_fn.getParamName(arg_index);
|
||||
const name_with_null = name.ptr[0 .. name.len + 1];
|
||||
const ty = emit.mir.function.air.getRefType(ty_str.ty);
|
||||
|
||||
switch (mcv) {
|
||||
.register => |reg| {
|
||||
|
||||
@ -15,7 +15,6 @@ const Decl = Module.Decl;
|
||||
const trace = @import("../tracy.zig").trace;
|
||||
const LazySrcLoc = Module.LazySrcLoc;
|
||||
const Air = @import("../Air.zig");
|
||||
const Zir = @import("../Zir.zig");
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
|
||||
const Mutability = enum { Const, Mut };
|
||||
@ -2807,49 +2806,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
}
|
||||
|
||||
fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
const air_datas = f.air.instructions.items(.data);
|
||||
const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
|
||||
const zir = f.object.dg.decl.getFileScope().zir;
|
||||
const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
|
||||
const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
|
||||
const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const args_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
_ = clobbers_len; // TODO honor these
|
||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]);
|
||||
const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]);
|
||||
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = f.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
|
||||
const clobbers_len = @truncate(u31, extra.data.flags);
|
||||
var extra_i: usize = extra.end;
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]);
|
||||
extra_i += outputs.len;
|
||||
const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]);
|
||||
extra_i += inputs.len;
|
||||
|
||||
if (outputs_len > 1) {
|
||||
if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none;
|
||||
|
||||
if (outputs.len > 1) {
|
||||
return f.fail("TODO implement codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
|
||||
if (f.liveness.isUnused(inst) and !is_volatile)
|
||||
return CValue.none;
|
||||
|
||||
var extra_i: usize = zir_extra.end;
|
||||
const output_constraint: ?[]const u8 = out: {
|
||||
var i: usize = 0;
|
||||
while (i < outputs_len) : (i += 1) {
|
||||
const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
|
||||
extra_i = output.end;
|
||||
break :out zir.nullTerminatedString(output.data.constraint);
|
||||
const output_constraint: ?[]const u8 = for (outputs) |output| {
|
||||
if (output != .none) {
|
||||
return f.fail("TODO implement codegen for non-expr asm", .{});
|
||||
}
|
||||
break :out null;
|
||||
};
|
||||
const args_extra_begin = extra_i;
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
break constraint;
|
||||
} else null;
|
||||
|
||||
const writer = f.object.writer();
|
||||
for (args) |arg| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
const inputs_extra_begin = extra_i;
|
||||
|
||||
for (inputs) |input| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
|
||||
const reg = constraint[1 .. constraint.len - 1];
|
||||
const arg_c_value = try f.resolveInst(arg);
|
||||
const arg_c_value = try f.resolveInst(input);
|
||||
try writer.writeAll("register ");
|
||||
try f.renderType(writer, f.air.typeOf(arg));
|
||||
try f.renderType(writer, f.air.typeOf(input));
|
||||
|
||||
try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg });
|
||||
try f.writeCValue(writer, arg_c_value);
|
||||
@ -2858,21 +2856,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return f.fail("TODO non-explicit inline asm regs", .{});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var clobber_i: u32 = 0;
|
||||
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
||||
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += clobber.len / 4 + 1;
|
||||
|
||||
// TODO honor these
|
||||
}
|
||||
}
|
||||
|
||||
const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
|
||||
const volatile_string: []const u8 = if (is_volatile) "volatile " else "";
|
||||
try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source });
|
||||
if (output_constraint) |_| {
|
||||
return f.fail("TODO: CBE inline asm output", .{});
|
||||
}
|
||||
if (args.len > 0) {
|
||||
if (inputs.len > 0) {
|
||||
if (output_constraint == null) {
|
||||
try writer.writeAll(" :");
|
||||
}
|
||||
try writer.writeAll(": ");
|
||||
extra_i = args_extra_begin;
|
||||
for (args) |_, index| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
extra_i = inputs_extra_begin;
|
||||
for (inputs) |_, index| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') {
|
||||
const reg = constraint[1 .. constraint.len - 1];
|
||||
if (index > 0) {
|
||||
|
||||
@ -2,24 +2,21 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Compilation = @import("../Compilation.zig");
|
||||
const llvm = @import("llvm/bindings.zig");
|
||||
const link = @import("../link.zig");
|
||||
const log = std.log.scoped(.codegen);
|
||||
const math = std.math;
|
||||
const native_endian = builtin.cpu.arch.endian();
|
||||
|
||||
const llvm = @import("llvm/bindings.zig");
|
||||
const link = @import("../link.zig");
|
||||
const Compilation = @import("../Compilation.zig");
|
||||
const build_options = @import("build_options");
|
||||
const Module = @import("../Module.zig");
|
||||
const TypedValue = @import("../TypedValue.zig");
|
||||
const Zir = @import("../Zir.zig");
|
||||
const Air = @import("../Air.zig");
|
||||
const Liveness = @import("../Liveness.zig");
|
||||
const target_util = @import("../target.zig");
|
||||
|
||||
const Value = @import("../value.zig").Value;
|
||||
const Type = @import("../type.zig").Type;
|
||||
|
||||
const LazySrcLoc = Module.LazySrcLoc;
|
||||
|
||||
const Error = error{ OutOfMemory, CodegenFail };
|
||||
@ -2895,33 +2892,21 @@ pub const FuncGen = struct {
|
||||
// as stage1.
|
||||
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const air_asm = self.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const zir = self.dg.decl.getFileScope().zir;
|
||||
const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
|
||||
const is_volatile = @truncate(u1, extended.small >> 15) != 0;
|
||||
if (!is_volatile and self.liveness.isUnused(inst)) {
|
||||
return null;
|
||||
}
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
if (outputs_len > 1) {
|
||||
const extra = self.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
|
||||
const clobbers_len = @truncate(u31, extra.data.flags);
|
||||
var extra_i: usize = extra.end;
|
||||
|
||||
if (!is_volatile and self.liveness.isUnused(inst)) return null;
|
||||
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]);
|
||||
extra_i += outputs.len;
|
||||
const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]);
|
||||
extra_i += inputs.len;
|
||||
|
||||
if (outputs.len > 1) {
|
||||
return self.todo("implement llvm codegen for asm with more than 1 output", .{});
|
||||
}
|
||||
const args_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
|
||||
const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
|
||||
const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..args_len]);
|
||||
|
||||
var extra_i: usize = zir_extra.end;
|
||||
const output_constraint: ?[]const u8 = out: {
|
||||
var i: usize = 0;
|
||||
while (i < outputs_len) : (i += 1) {
|
||||
const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
|
||||
extra_i = output.end;
|
||||
break :out zir.nullTerminatedString(output.data.constraint);
|
||||
}
|
||||
break :out null;
|
||||
};
|
||||
|
||||
var llvm_constraints: std.ArrayListUnmanaged(u8) = .{};
|
||||
defer llvm_constraints.deinit(self.gpa);
|
||||
@ -2930,14 +2915,21 @@ pub const FuncGen = struct {
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
const llvm_params_len = args.len;
|
||||
const llvm_params_len = inputs.len;
|
||||
const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
|
||||
const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
|
||||
|
||||
var llvm_param_i: usize = 0;
|
||||
var total_i: usize = 0;
|
||||
|
||||
if (output_constraint) |constraint| {
|
||||
for (outputs) |output| {
|
||||
if (output != .none) {
|
||||
return self.todo("implement inline asm with non-returned output", .{});
|
||||
}
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1);
|
||||
if (total_i != 0) {
|
||||
llvm_constraints.appendAssumeCapacity(',');
|
||||
@ -2948,11 +2940,13 @@ pub const FuncGen = struct {
|
||||
total_i += 1;
|
||||
}
|
||||
|
||||
for (args) |arg| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
const arg_llvm_value = try self.resolveInst(arg);
|
||||
for (inputs) |input| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
const arg_llvm_value = try self.resolveInst(input);
|
||||
|
||||
llvm_param_values[llvm_param_i] = arg_llvm_value;
|
||||
llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf();
|
||||
@ -2967,19 +2961,26 @@ pub const FuncGen = struct {
|
||||
total_i += 1;
|
||||
}
|
||||
|
||||
const clobbers = zir.extra[extra_i..][0..clobbers_len];
|
||||
for (clobbers) |clobber_index| {
|
||||
const clobber = zir.nullTerminatedString(clobber_index);
|
||||
try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
|
||||
if (total_i != 0) {
|
||||
llvm_constraints.appendAssumeCapacity(',');
|
||||
}
|
||||
llvm_constraints.appendSliceAssumeCapacity("~{");
|
||||
llvm_constraints.appendSliceAssumeCapacity(clobber);
|
||||
llvm_constraints.appendSliceAssumeCapacity("}");
|
||||
{
|
||||
var clobber_i: u32 = 0;
|
||||
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
||||
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += clobber.len / 4 + 1;
|
||||
|
||||
total_i += 1;
|
||||
try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4);
|
||||
if (total_i != 0) {
|
||||
llvm_constraints.appendAssumeCapacity(',');
|
||||
}
|
||||
llvm_constraints.appendSliceAssumeCapacity("~{");
|
||||
llvm_constraints.appendSliceAssumeCapacity(clobber);
|
||||
llvm_constraints.appendSliceAssumeCapacity("}");
|
||||
|
||||
total_i += 1;
|
||||
}
|
||||
}
|
||||
const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
|
||||
const ret_ty = self.air.typeOfIndex(inst);
|
||||
const ret_llvm_ty = try self.dg.llvmType(ret_ty);
|
||||
|
||||
@ -4,11 +4,10 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
|
||||
|
||||
const Module = @import("Module.zig");
|
||||
const Value = @import("value.zig").Value;
|
||||
const Zir = @import("Zir.zig");
|
||||
const Air = @import("Air.zig");
|
||||
const Liveness = @import("Liveness.zig");
|
||||
|
||||
pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void {
|
||||
pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void {
|
||||
const instruction_bytes = air.instructions.len *
|
||||
// Here we don't use @sizeOf(Air.Inst.Data) because it would include
|
||||
// the debug safety tag but we want to measure release size.
|
||||
@ -49,7 +48,6 @@ pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void {
|
||||
.gpa = gpa,
|
||||
.arena = arena.allocator(),
|
||||
.air = air,
|
||||
.zir = zir,
|
||||
.liveness = liveness,
|
||||
.indent = 2,
|
||||
};
|
||||
@ -63,7 +61,6 @@ const Writer = struct {
|
||||
gpa: Allocator,
|
||||
arena: Allocator,
|
||||
air: Air,
|
||||
zir: Zir,
|
||||
liveness: Liveness,
|
||||
indent: usize,
|
||||
|
||||
@ -100,8 +97,6 @@ const Writer = struct {
|
||||
const tag = tags[inst];
|
||||
try s.print("= {s}(", .{@tagName(tags[inst])});
|
||||
switch (tag) {
|
||||
.arg => try w.writeTyStr(s, inst),
|
||||
|
||||
.add,
|
||||
.addwrap,
|
||||
.add_sat,
|
||||
@ -181,6 +176,7 @@ const Writer = struct {
|
||||
.const_ty,
|
||||
.alloc,
|
||||
.ret_ptr,
|
||||
.arg,
|
||||
=> try w.writeTy(s, inst),
|
||||
|
||||
.not,
|
||||
@ -259,12 +255,6 @@ const Writer = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const ty_str = w.air.instructions.items(.data)[inst].ty_str;
|
||||
const name = w.zir.nullTerminatedString(ty_str.str);
|
||||
try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), w.air.getRefType(ty_str.ty) });
|
||||
}
|
||||
|
||||
fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const bin_op = w.air.instructions.items(.data)[inst].bin_op;
|
||||
try w.writeOperand(s, inst, 0, bin_op.lhs);
|
||||
@ -440,51 +430,67 @@ const Writer = struct {
|
||||
|
||||
fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
|
||||
const air_asm = w.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const zir = w.zir;
|
||||
const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
|
||||
const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
|
||||
const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
|
||||
const outputs_len = @truncate(u5, extended.small);
|
||||
const args_len = @truncate(u5, extended.small >> 5);
|
||||
const clobbers_len = @truncate(u5, extended.small >> 10);
|
||||
const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]);
|
||||
const extra = w.air.extraData(Air.Asm, ty_pl.payload);
|
||||
const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0;
|
||||
const clobbers_len = @truncate(u31, extra.data.flags);
|
||||
var extra_i: usize = extra.end;
|
||||
var op_index: usize = 0;
|
||||
|
||||
var extra_i: usize = zir_extra.end;
|
||||
const output_constraint: ?[]const u8 = out: {
|
||||
var i: usize = 0;
|
||||
while (i < outputs_len) : (i += 1) {
|
||||
const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
|
||||
extra_i = output.end;
|
||||
break :out zir.nullTerminatedString(output.data.constraint);
|
||||
}
|
||||
break :out null;
|
||||
};
|
||||
const ret_ty = w.air.typeOfIndex(inst);
|
||||
try s.print("{}", .{ret_ty});
|
||||
|
||||
try s.print("\"{s}\"", .{asm_source});
|
||||
|
||||
if (output_constraint) |constraint| {
|
||||
const ret_ty = w.air.typeOfIndex(inst);
|
||||
try s.print(", {s} -> {}", .{ constraint, ret_ty });
|
||||
if (is_volatile) {
|
||||
try s.writeAll(", volatile");
|
||||
}
|
||||
|
||||
for (args) |arg| {
|
||||
const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
|
||||
extra_i = input.end;
|
||||
const constraint = zir.nullTerminatedString(input.data.constraint);
|
||||
const outputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]);
|
||||
extra_i += outputs.len;
|
||||
const inputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]);
|
||||
extra_i += inputs.len;
|
||||
|
||||
try s.print(", {s} = (", .{constraint});
|
||||
try w.writeOperand(s, inst, 0, arg);
|
||||
for (outputs) |output| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
if (output == .none) {
|
||||
try s.print(", -> {s}", .{constraint});
|
||||
} else {
|
||||
try s.print(", out {s} = (", .{constraint});
|
||||
try w.writeOperand(s, inst, op_index, output);
|
||||
op_index += 1;
|
||||
try s.writeByte(')');
|
||||
}
|
||||
}
|
||||
|
||||
for (inputs) |input| {
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += constraint.len / 4 + 1;
|
||||
|
||||
try s.print(", in {s} = (", .{constraint});
|
||||
try w.writeOperand(s, inst, op_index, input);
|
||||
op_index += 1;
|
||||
try s.writeByte(')');
|
||||
}
|
||||
|
||||
const clobbers = zir.extra[extra_i..][0..clobbers_len];
|
||||
for (clobbers) |clobber_index| {
|
||||
const clobber = zir.nullTerminatedString(clobber_index);
|
||||
try s.writeAll(", ~{");
|
||||
try s.writeAll(clobber);
|
||||
try s.writeAll("}");
|
||||
{
|
||||
var clobber_i: u32 = 0;
|
||||
while (clobber_i < clobbers_len) : (clobber_i += 1) {
|
||||
const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0);
|
||||
// This equation accounts for the fact that even if we have exactly 4 bytes
|
||||
// for the string, we still use the next u32 for the null terminator.
|
||||
extra_i += clobber.len / 4 + 1;
|
||||
|
||||
try s.writeAll(", ~{");
|
||||
try s.writeAll(clobber);
|
||||
try s.writeAll("}");
|
||||
}
|
||||
}
|
||||
const asm_source = std.mem.sliceAsBytes(w.air.extra[extra_i..])[0..extra.data.source_len];
|
||||
try s.print(", \"{s}\"", .{asm_source});
|
||||
}
|
||||
|
||||
fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user