mirror of
https://github.com/ziglang/zig.git
synced 2026-01-30 11:13:38 +00:00
Merge pull request #11783 from ziglang/stage2-try
introduce a "try" ZIR and AIR instruction
This commit is contained in:
commit
d1bfc83774
33
src/Air.zig
33
src/Air.zig
@ -320,6 +320,20 @@ pub const Inst = struct {
|
||||
/// Result type is always noreturn; no instructions in a block follow this one.
|
||||
/// Uses the `pl_op` field. Operand is the condition. Payload is `SwitchBr`.
|
||||
switch_br,
|
||||
/// Given an operand which is an error union, splits control flow. In
|
||||
/// case of error, control flow goes into the block that is part of this
|
||||
/// instruction, which is guaranteed to end with a return instruction
|
||||
/// and never breaks out of the block.
|
||||
/// In the case of non-error, control flow proceeds to the next instruction
|
||||
/// after the `try`, with the result of this instruction being the unwrapped
|
||||
/// payload value, as if `unwrap_errunion_payload` was executed on the operand.
|
||||
/// Uses the `pl_op` field. Payload is `Try`.
|
||||
@"try",
|
||||
/// Same as `try` except the operand is a pointer to an error union, and the
|
||||
/// result is a pointer to the payload. Result is as if `unwrap_errunion_payload_ptr`
|
||||
/// was executed on the operand.
|
||||
/// Uses the `ty_pl` field. Payload is `TryPtr`.
|
||||
try_ptr,
|
||||
/// A comptime-known value. Uses the `ty_pl` field, payload is index of
|
||||
/// `values` array.
|
||||
constant,
|
||||
@ -780,6 +794,19 @@ pub const SwitchBr = struct {
|
||||
};
|
||||
};
|
||||
|
||||
/// This data is stored inside extra. Trailing:
|
||||
/// 0. body: Inst.Index // for each body_len
|
||||
pub const Try = struct {
|
||||
body_len: u32,
|
||||
};
|
||||
|
||||
/// This data is stored inside extra. Trailing:
|
||||
/// 0. body: Inst.Index // for each body_len
|
||||
pub const TryPtr = struct {
|
||||
ptr: Inst.Ref,
|
||||
body_len: u32,
|
||||
};
|
||||
|
||||
pub const StructField = struct {
|
||||
/// Whether this is a pointer or byval is determined by the AIR tag.
|
||||
struct_operand: Inst.Ref,
|
||||
@ -991,6 +1018,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
.shl_with_overflow,
|
||||
.ptr_add,
|
||||
.ptr_sub,
|
||||
.try_ptr,
|
||||
=> return air.getRefType(datas[inst].ty_pl.ty),
|
||||
|
||||
.not,
|
||||
@ -1102,6 +1130,11 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
|
||||
const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data;
|
||||
return air.typeOf(extra.lhs);
|
||||
},
|
||||
|
||||
.@"try" => {
|
||||
const err_union_ty = air.typeOf(datas[inst].pl_op.operand);
|
||||
return err_union_ty.errorUnionPayload();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2425,6 +2425,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
|
||||
.param_type,
|
||||
.ret_ptr,
|
||||
.ret_type,
|
||||
.@"try",
|
||||
.try_ptr,
|
||||
//.try_inline,
|
||||
//.try_ptr_inline,
|
||||
=> break :b false,
|
||||
|
||||
.extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
|
||||
@ -4871,68 +4875,43 @@ fn tryExpr(
|
||||
|
||||
if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});
|
||||
|
||||
var block_scope = parent_gz.makeSubBlock(scope);
|
||||
block_scope.setBreakResultLoc(rl);
|
||||
defer block_scope.unstack();
|
||||
|
||||
const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
|
||||
const operand_rl: ResultLoc = switch (rl) {
|
||||
.ref => .ref,
|
||||
else => .none,
|
||||
};
|
||||
const err_ops = switch (operand_rl) {
|
||||
// zig fmt: off
|
||||
.ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },
|
||||
else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe },
|
||||
// zig fmt: on
|
||||
// This could be a pointer or value depending on the `rl` parameter.
|
||||
const operand = try expr(parent_gz, scope, operand_rl, operand_node);
|
||||
const is_inline = parent_gz.force_comptime;
|
||||
const is_inline_bit = @as(u2, @boolToInt(is_inline));
|
||||
const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1;
|
||||
const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) {
|
||||
0b00 => .@"try",
|
||||
0b01 => .@"try",
|
||||
//0b01 => .try_inline,
|
||||
0b10 => .try_ptr,
|
||||
0b11 => .try_ptr,
|
||||
//0b11 => .try_ptr_inline,
|
||||
};
|
||||
// This could be a pointer or value depending on the `operand_rl` parameter.
|
||||
// We cannot use `block_scope.break_result_loc` because that has the bare
|
||||
// type, whereas this expression has the optional type. Later we make
|
||||
// up for this fact by calling rvalue on the else branch.
|
||||
const operand = try expr(&block_scope, &block_scope.base, operand_rl, operand_node);
|
||||
const cond = try block_scope.addUnNode(err_ops[0], operand, node);
|
||||
const condbr = try block_scope.addCondBr(.condbr, node);
|
||||
const try_inst = try parent_gz.makeBlockInst(block_tag, node);
|
||||
try parent_gz.instructions.append(astgen.gpa, try_inst);
|
||||
|
||||
const block = try parent_gz.makeBlockInst(.block, node);
|
||||
try block_scope.setBlockBody(block);
|
||||
// block_scope unstacked now, can add new instructions to parent_gz
|
||||
try parent_gz.instructions.append(astgen.gpa, block);
|
||||
|
||||
var then_scope = parent_gz.makeSubBlock(scope);
|
||||
defer then_scope.unstack();
|
||||
|
||||
block_scope.break_count += 1;
|
||||
// This could be a pointer or value depending on `err_ops[2]`.
|
||||
const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node);
|
||||
const then_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
|
||||
// else_scope will be stacked on then_scope as both are stacked on parent_gz
|
||||
var else_scope = parent_gz.makeSubBlock(scope);
|
||||
defer else_scope.unstack();
|
||||
|
||||
const err_code = try else_scope.addUnNode(err_ops[1], operand, node);
|
||||
const err_tag = switch (rl) {
|
||||
.ref => Zir.Inst.Tag.err_union_code_ptr,
|
||||
else => Zir.Inst.Tag.err_union_code,
|
||||
};
|
||||
const err_code = try else_scope.addUnNode(err_tag, operand, node);
|
||||
try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
|
||||
const else_result = try else_scope.addUnNode(.ret_node, err_code, node);
|
||||
_ = try else_scope.addUnNode(.ret_node, err_code, node);
|
||||
|
||||
const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
|
||||
return finishThenElseBlock(
|
||||
parent_gz,
|
||||
rl,
|
||||
node,
|
||||
&block_scope,
|
||||
&then_scope,
|
||||
&else_scope,
|
||||
condbr,
|
||||
cond,
|
||||
then_result,
|
||||
else_result,
|
||||
block,
|
||||
block,
|
||||
break_tag,
|
||||
);
|
||||
try else_scope.setTryBody(try_inst, operand);
|
||||
const result = indexToRef(try_inst);
|
||||
switch (rl) {
|
||||
.ref => return result,
|
||||
else => return rvalue(parent_gz, rl, result, node),
|
||||
}
|
||||
}
|
||||
|
||||
fn orelseCatchExpr(
|
||||
@ -10018,6 +9997,22 @@ const GenZir = struct {
|
||||
gz.unstack();
|
||||
}
|
||||
|
||||
/// Assumes nothing stacked on `gz`. Unstacks `gz`.
|
||||
fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
|
||||
const gpa = gz.astgen.gpa;
|
||||
const body = gz.instructionsSlice();
|
||||
try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Try).Struct.fields.len + body.len);
|
||||
const zir_datas = gz.astgen.instructions.items(.data);
|
||||
zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity(
|
||||
Zir.Inst.Try{
|
||||
.operand = operand,
|
||||
.body_len = @intCast(u32, body.len),
|
||||
},
|
||||
);
|
||||
gz.astgen.extra.appendSliceAssumeCapacity(body);
|
||||
gz.unstack();
|
||||
}
|
||||
|
||||
/// Must be called with the following stack set up:
|
||||
/// * gz (bottom)
|
||||
/// * align_gz
|
||||
|
||||
@ -478,6 +478,12 @@ pub fn categorizeOperand(
|
||||
.block => {
|
||||
return .complex;
|
||||
},
|
||||
.@"try" => {
|
||||
return .complex;
|
||||
},
|
||||
.try_ptr => {
|
||||
return .complex;
|
||||
},
|
||||
.loop => {
|
||||
return .complex;
|
||||
},
|
||||
@ -1019,6 +1025,19 @@ fn analyzeInst(
|
||||
try analyzeWithContext(a, new_set, body);
|
||||
return; // Loop has no operands and it is always unreferenced.
|
||||
},
|
||||
.@"try" => {
|
||||
const pl_op = inst_datas[inst].pl_op;
|
||||
const extra = a.air.extraData(Air.Try, pl_op.payload);
|
||||
const body = a.air.extra[extra.end..][0..extra.data.body_len];
|
||||
try analyzeWithContext(a, new_set, body);
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, .none, .none });
|
||||
},
|
||||
.try_ptr => {
|
||||
const extra = a.air.extraData(Air.TryPtr, inst_datas[inst].ty_pl.payload);
|
||||
const body = a.air.extra[extra.end..][0..extra.data.body_len];
|
||||
try analyzeWithContext(a, new_set, body);
|
||||
return trackOperands(a, new_set, inst, main_tomb, .{ extra.data.ptr, .none, .none });
|
||||
},
|
||||
.cond_br => {
|
||||
// Each death that occurs inside one branch, but not the other, needs
|
||||
// to be added as a death immediately upon entering the other branch.
|
||||
|
||||
249
src/Sema.zig
249
src/Sema.zig
@ -1322,6 +1322,106 @@ fn analyzeBodyInner(
|
||||
break break_data.inst;
|
||||
}
|
||||
},
|
||||
.@"try" => blk: {
|
||||
if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
|
||||
const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union = try sema.resolveInst(extra.data.operand);
|
||||
const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
|
||||
assert(is_non_err != .none);
|
||||
const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
|
||||
if (is_non_err_tv.val.toBool()) {
|
||||
const err_union_ty = sema.typeOf(err_union);
|
||||
break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
|
||||
}
|
||||
const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
|
||||
break always_noreturn;
|
||||
if (inst == break_data.block_inst) {
|
||||
break :blk try sema.resolveInst(break_data.operand);
|
||||
} else {
|
||||
break break_data.inst;
|
||||
}
|
||||
},
|
||||
//.try_inline => blk: {
|
||||
// const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
// const src = inst_data.src();
|
||||
// const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
// const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
|
||||
// const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
// const operand = try sema.resolveInst(extra.data.operand);
|
||||
// const operand_ty = sema.typeOf(operand);
|
||||
// const is_ptr = operand_ty.zigTypeTag() == .Pointer;
|
||||
// const err_union = if (is_ptr)
|
||||
// try sema.analyzeLoad(block, src, operand, operand_src)
|
||||
// else
|
||||
// operand;
|
||||
// const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
|
||||
// assert(is_non_err != .none);
|
||||
// const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
|
||||
// if (is_non_err_tv.val.toBool()) {
|
||||
// if (is_ptr) {
|
||||
// break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
|
||||
// } else {
|
||||
// const err_union_ty = sema.typeOf(err_union);
|
||||
// break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false);
|
||||
// }
|
||||
// }
|
||||
// const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
|
||||
// break always_noreturn;
|
||||
// if (inst == break_data.block_inst) {
|
||||
// break :blk try sema.resolveInst(break_data.operand);
|
||||
// } else {
|
||||
// break break_data.inst;
|
||||
// }
|
||||
//},
|
||||
.try_ptr => blk: {
|
||||
if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst);
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
|
||||
const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
const operand = try sema.resolveInst(extra.data.operand);
|
||||
const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
|
||||
const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
|
||||
assert(is_non_err != .none);
|
||||
const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
|
||||
if (is_non_err_tv.val.toBool()) {
|
||||
break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
|
||||
}
|
||||
const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
|
||||
break always_noreturn;
|
||||
if (inst == break_data.block_inst) {
|
||||
break :blk try sema.resolveInst(break_data.operand);
|
||||
} else {
|
||||
break break_data.inst;
|
||||
}
|
||||
},
|
||||
//.try_ptr_inline => blk: {
|
||||
// const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
// const src = inst_data.src();
|
||||
// const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
// const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
|
||||
// const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
// const operand = try sema.resolveInst(extra.data.operand);
|
||||
// const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
|
||||
// const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
|
||||
// assert(is_non_err != .none);
|
||||
// const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
|
||||
// if (is_non_err_tv.val.toBool()) {
|
||||
// break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
|
||||
// }
|
||||
// const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
|
||||
// break always_noreturn;
|
||||
// if (inst == break_data.block_inst) {
|
||||
// break :blk try sema.resolveInst(break_data.operand);
|
||||
// } else {
|
||||
// break break_data.inst;
|
||||
// }
|
||||
//},
|
||||
};
|
||||
if (sema.typeOf(air_inst).isNoReturn())
|
||||
break always_noreturn;
|
||||
@ -6426,32 +6526,43 @@ fn zirErrUnionPayload(
|
||||
const src = inst_data.src();
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
const operand_src = src;
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
if (operand_ty.zigTypeTag() != .ErrorUnion) {
|
||||
const err_union_ty = sema.typeOf(operand);
|
||||
if (err_union_ty.zigTypeTag() != .ErrorUnion) {
|
||||
return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
|
||||
operand_ty.fmt(sema.mod),
|
||||
err_union_ty.fmt(sema.mod),
|
||||
});
|
||||
}
|
||||
return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, safety_check);
|
||||
}
|
||||
|
||||
const result_ty = operand_ty.errorUnionPayload();
|
||||
if (try sema.resolveDefinedValue(block, src, operand)) |val| {
|
||||
fn analyzeErrUnionPayload(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
err_union_ty: Type,
|
||||
operand: Zir.Inst.Ref,
|
||||
operand_src: LazySrcLoc,
|
||||
safety_check: bool,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const payload_ty = err_union_ty.errorUnionPayload();
|
||||
if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
|
||||
if (val.getError()) |name| {
|
||||
return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
|
||||
}
|
||||
const data = val.castTag(.eu_payload).?.data;
|
||||
return sema.addConstant(result_ty, data);
|
||||
return sema.addConstant(payload_ty, data);
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
|
||||
// If the error set has no fields then no safety check is needed.
|
||||
if (safety_check and block.wantSafety() and
|
||||
operand_ty.errorUnionSet().errorSetCardinality() != .zero)
|
||||
err_union_ty.errorUnionSet().errorSetCardinality() != .zero)
|
||||
{
|
||||
try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
|
||||
}
|
||||
|
||||
return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
|
||||
return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
|
||||
}
|
||||
|
||||
/// Pointer in, pointer out.
|
||||
@ -12969,6 +13080,108 @@ fn zirCondbr(
|
||||
return always_noreturn;
|
||||
}
|
||||
|
||||
fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
|
||||
const body = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union = try sema.resolveInst(extra.data.operand);
|
||||
const err_union_ty = sema.typeOf(err_union);
|
||||
if (err_union_ty.zigTypeTag() != .ErrorUnion) {
|
||||
return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
|
||||
err_union_ty.fmt(sema.mod),
|
||||
});
|
||||
}
|
||||
const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
|
||||
if (is_non_err != .none) {
|
||||
const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
|
||||
if (is_non_err_val.toBool()) {
|
||||
return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
|
||||
}
|
||||
// We can analyze the body directly in the parent block because we know there are
|
||||
// no breaks from the body possible, and that the body is noreturn.
|
||||
return sema.resolveBody(parent_block, body, inst);
|
||||
}
|
||||
|
||||
var sub_block = parent_block.makeSubBlock();
|
||||
defer sub_block.instructions.deinit(sema.gpa);
|
||||
|
||||
// This body is guaranteed to end with noreturn and has no breaks.
|
||||
_ = try sema.analyzeBodyInner(&sub_block, body);
|
||||
|
||||
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
|
||||
sub_block.instructions.items.len);
|
||||
const try_inst = try parent_block.addInst(.{
|
||||
.tag = .@"try",
|
||||
.data = .{ .pl_op = .{
|
||||
.operand = err_union,
|
||||
.payload = sema.addExtraAssumeCapacity(Air.Try{
|
||||
.body_len = @intCast(u32, sub_block.instructions.items.len),
|
||||
}),
|
||||
} },
|
||||
});
|
||||
sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
|
||||
return try_inst;
|
||||
}
|
||||
|
||||
fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
|
||||
const body = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
const operand = try sema.resolveInst(extra.data.operand);
|
||||
const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
|
||||
const err_union_ty = sema.typeOf(err_union);
|
||||
if (err_union_ty.zigTypeTag() != .ErrorUnion) {
|
||||
return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
|
||||
err_union_ty.fmt(sema.mod),
|
||||
});
|
||||
}
|
||||
const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
|
||||
if (is_non_err != .none) {
|
||||
const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
|
||||
if (is_non_err_val.toBool()) {
|
||||
return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
|
||||
}
|
||||
// We can analyze the body directly in the parent block because we know there are
|
||||
// no breaks from the body possible, and that the body is noreturn.
|
||||
return sema.resolveBody(parent_block, body, inst);
|
||||
}
|
||||
|
||||
var sub_block = parent_block.makeSubBlock();
|
||||
defer sub_block.instructions.deinit(sema.gpa);
|
||||
|
||||
// This body is guaranteed to end with noreturn and has no breaks.
|
||||
_ = try sema.analyzeBodyInner(&sub_block, body);
|
||||
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
const ptr_info = operand_ty.ptrInfo().data;
|
||||
const res_ty = try Type.ptr(sema.arena, sema.mod, .{
|
||||
.pointee_type = err_union_ty.errorUnionPayload(),
|
||||
.@"addrspace" = ptr_info.@"addrspace",
|
||||
.mutable = ptr_info.mutable,
|
||||
.@"allowzero" = ptr_info.@"allowzero",
|
||||
.@"volatile" = ptr_info.@"volatile",
|
||||
});
|
||||
const res_ty_ref = try sema.addType(res_ty);
|
||||
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len +
|
||||
sub_block.instructions.items.len);
|
||||
const try_inst = try parent_block.addInst(.{
|
||||
.tag = .try_ptr,
|
||||
.data = .{ .ty_pl = .{
|
||||
.ty = res_ty_ref,
|
||||
.payload = sema.addExtraAssumeCapacity(Air.TryPtr{
|
||||
.ptr = operand,
|
||||
.body_len = @intCast(u32, sub_block.instructions.items.len),
|
||||
}),
|
||||
} },
|
||||
});
|
||||
sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
|
||||
return try_inst;
|
||||
}
|
||||
|
||||
// A `break` statement is inside a runtime condition, but trying to
|
||||
// break from an inline loop. In such case we must convert it to
|
||||
// a runtime break.
|
||||
@ -21622,7 +21835,7 @@ fn analyzeIsNull(
|
||||
return block.addUnOp(air_tag, operand);
|
||||
}
|
||||
|
||||
fn analyzeIsNonErr(
|
||||
fn analyzeIsNonErrComptimeOnly(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
@ -21674,8 +21887,22 @@ fn analyzeIsNonErr(
|
||||
return Air.Inst.Ref.bool_false;
|
||||
}
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(.is_non_err, operand);
|
||||
return Air.Inst.Ref.none;
|
||||
}
|
||||
|
||||
fn analyzeIsNonErr(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
operand: Air.Inst.Ref,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
|
||||
if (result == .none) {
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(.is_non_err, operand);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn analyzeSlice(
|
||||
|
||||
43
src/Zir.zig
43
src/Zir.zig
@ -319,6 +319,23 @@ pub const Inst = struct {
|
||||
/// only the taken branch is analyzed. The then block and else block must
|
||||
/// terminate with an "inline" variant of a noreturn instruction.
|
||||
condbr_inline,
|
||||
/// Given an operand which is an error union, splits control flow. In
|
||||
/// case of error, control flow goes into the block that is part of this
|
||||
/// instruction, which is guaranteed to end with a return instruction
|
||||
/// and never breaks out of the block.
|
||||
/// In the case of non-error, control flow proceeds to the next instruction
|
||||
/// after the `try`, with the result of this instruction being the unwrapped
|
||||
/// payload value, as if `err_union_payload_unsafe` was executed on the operand.
|
||||
/// Uses the `pl_node` union field. Payload is `Try`.
|
||||
@"try",
|
||||
///// Same as `try` except the operand is coerced to a comptime value, and
|
||||
///// only the taken branch is analyzed. The block must terminate with an "inline"
|
||||
///// variant of a noreturn instruction.
|
||||
//try_inline,
|
||||
/// Same as `try` except the operand is a pointer and the result is a pointer.
|
||||
try_ptr,
|
||||
///// Same as `try_inline` except the operand is a pointer and the result is a pointer.
|
||||
//try_ptr_inline,
|
||||
/// An error set type definition. Contains a list of field names.
|
||||
/// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
|
||||
error_set_decl,
|
||||
@ -1231,6 +1248,10 @@ pub const Inst = struct {
|
||||
.closure_capture,
|
||||
.ret_ptr,
|
||||
.ret_type,
|
||||
.@"try",
|
||||
.try_ptr,
|
||||
//.try_inline,
|
||||
//.try_ptr_inline,
|
||||
=> false,
|
||||
|
||||
.@"break",
|
||||
@ -1509,6 +1530,10 @@ pub const Inst = struct {
|
||||
.repeat,
|
||||
.repeat_inline,
|
||||
.panic,
|
||||
.@"try",
|
||||
.try_ptr,
|
||||
//.try_inline,
|
||||
//.try_ptr_inline,
|
||||
=> false,
|
||||
|
||||
.extended => switch (data.extended.opcode) {
|
||||
@ -1569,6 +1594,10 @@ pub const Inst = struct {
|
||||
.coerce_result_ptr = .bin,
|
||||
.condbr = .pl_node,
|
||||
.condbr_inline = .pl_node,
|
||||
.@"try" = .pl_node,
|
||||
.try_ptr = .pl_node,
|
||||
//.try_inline = .pl_node,
|
||||
//.try_ptr_inline = .pl_node,
|
||||
.error_set_decl = .pl_node,
|
||||
.error_set_decl_anon = .pl_node,
|
||||
.error_set_decl_func = .pl_node,
|
||||
@ -2808,6 +2837,14 @@ pub const Inst = struct {
|
||||
else_body_len: u32,
|
||||
};
|
||||
|
||||
/// This data is stored inside extra, trailed by:
|
||||
/// * 0. body: Index // for each `body_len`.
|
||||
pub const Try = struct {
|
||||
/// The error union to unwrap.
|
||||
operand: Ref,
|
||||
body_len: u32,
|
||||
};
|
||||
|
||||
/// Stored in extra. Depending on the flags in Data, there will be up to 5
|
||||
/// trailing Ref fields:
|
||||
/// 0. sentinel: Ref // if `has_sentinel` flag is set
|
||||
@ -3744,6 +3781,12 @@ fn findDeclsInner(
|
||||
try zir.findDeclsBody(list, then_body);
|
||||
try zir.findDeclsBody(list, else_body);
|
||||
},
|
||||
.@"try", .try_ptr => {
|
||||
const inst_data = datas[inst].pl_node;
|
||||
const extra = zir.extraData(Inst.Try, inst_data.payload_index);
|
||||
const body = zir.extra[extra.end..][0..extra.data.body_len];
|
||||
try zir.findDeclsBody(list, body);
|
||||
},
|
||||
.switch_block => return findDeclsSwitch(zir, list, inst),
|
||||
|
||||
.suspend_block => @panic("TODO iterate suspend block"),
|
||||
|
||||
@ -665,6 +665,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
.mul_add => try self.airMulAdd(inst),
|
||||
|
||||
.@"try" => try self.airTry(inst),
|
||||
.try_ptr => try self.airTryPtr(inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
@ -2305,27 +2308,70 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
/// Given an error union, returns the error
|
||||
fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
|
||||
const err_ty = error_union_ty.errorUnionSet();
|
||||
const payload_ty = error_union_ty.errorUnionPayload();
|
||||
if (err_ty.errorSetCardinality() == .zero) {
|
||||
return MCValue{ .immediate = 0 };
|
||||
}
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
return error_union_mcv;
|
||||
}
|
||||
|
||||
const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));
|
||||
switch (error_union_mcv) {
|
||||
.register => return self.fail("TODO errUnionErr for registers", .{}),
|
||||
.stack_offset => |off| {
|
||||
return MCValue{ .stack_offset = off - err_offset };
|
||||
},
|
||||
.memory => |addr| {
|
||||
return MCValue{ .memory = addr + err_offset };
|
||||
},
|
||||
else => unreachable, // invalid MCValue for an error union
|
||||
}
|
||||
}
|
||||
|
||||
fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
const error_union_ty = self.air.typeOf(ty_op.operand);
|
||||
const payload_ty = error_union_ty.errorUnionPayload();
|
||||
const mcv = try self.resolveInst(ty_op.operand);
|
||||
if (!payload_ty.hasRuntimeBits()) break :result mcv;
|
||||
|
||||
return self.fail("TODO implement unwrap error union error for non-empty payloads", .{});
|
||||
break :result try self.errUnionErr(mcv, error_union_ty);
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
/// Given an error union, returns the payload
|
||||
fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
|
||||
const err_ty = error_union_ty.errorUnionSet();
|
||||
const payload_ty = error_union_ty.errorUnionPayload();
|
||||
if (err_ty.errorSetCardinality() == .zero) {
|
||||
return error_union_mcv;
|
||||
}
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
return MCValue.none;
|
||||
}
|
||||
|
||||
const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
|
||||
switch (error_union_mcv) {
|
||||
.register => return self.fail("TODO errUnionPayload for registers", .{}),
|
||||
.stack_offset => |off| {
|
||||
return MCValue{ .stack_offset = off - payload_offset };
|
||||
},
|
||||
.memory => |addr| {
|
||||
return MCValue{ .memory = addr + payload_offset };
|
||||
},
|
||||
else => unreachable, // invalid MCValue for an error union
|
||||
}
|
||||
}
|
||||
|
||||
fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
const error_union_ty = self.air.typeOf(ty_op.operand);
|
||||
const payload_ty = error_union_ty.errorUnionPayload();
|
||||
if (!payload_ty.hasRuntimeBits()) break :result MCValue.none;
|
||||
|
||||
return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{});
|
||||
const error_union = try self.resolveInst(ty_op.operand);
|
||||
break :result try self.errUnionPayload(error_union, error_union_ty);
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
@ -3386,45 +3432,38 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, .dead, .{ operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const cond = try self.resolveInst(pl_op.operand);
|
||||
const extra = self.air.extraData(Air.CondBr, pl_op.payload);
|
||||
const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
|
||||
const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
|
||||
const liveness_condbr = self.liveness.getCondBr(inst);
|
||||
|
||||
const reloc: Mir.Inst.Index = switch (cond) {
|
||||
fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
|
||||
switch (condition) {
|
||||
.compare_flags_signed,
|
||||
.compare_flags_unsigned,
|
||||
=> try self.addInst(.{
|
||||
=> return try self.addInst(.{
|
||||
.tag = .b_cond,
|
||||
.data = .{
|
||||
.inst_cond = .{
|
||||
.inst = undefined, // populated later through performReloc
|
||||
.cond = switch (cond) {
|
||||
.cond = switch (condition) {
|
||||
.compare_flags_signed => |cmp_op| blk: {
|
||||
// Here we map to the opposite condition because the jump is to the false branch.
|
||||
const condition = Instruction.Condition.fromCompareOperatorSigned(cmp_op);
|
||||
break :blk condition.negate();
|
||||
const condition_code = Instruction.Condition.fromCompareOperatorSigned(cmp_op);
|
||||
break :blk condition_code.negate();
|
||||
},
|
||||
.compare_flags_unsigned => |cmp_op| blk: {
|
||||
// Here we map to the opposite condition because the jump is to the false branch.
|
||||
const condition = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op);
|
||||
break :blk condition.negate();
|
||||
const condition_code = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op);
|
||||
break :blk condition_code.negate();
|
||||
},
|
||||
else => unreachable,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
else => blk: {
|
||||
const reg = switch (cond) {
|
||||
else => {
|
||||
const reg = switch (condition) {
|
||||
.register => |r| r,
|
||||
else => try self.copyToTmpRegister(Type.bool, cond),
|
||||
else => try self.copyToTmpRegister(Type.bool, condition),
|
||||
};
|
||||
|
||||
break :blk try self.addInst(.{
|
||||
return try self.addInst(.{
|
||||
.tag = .cbz,
|
||||
.data = .{
|
||||
.r_inst = .{
|
||||
@ -3434,7 +3473,18 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const cond = try self.resolveInst(pl_op.operand);
|
||||
const extra = self.air.extraData(Air.CondBr, pl_op.payload);
|
||||
const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
|
||||
const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
|
||||
const liveness_condbr = self.liveness.getCondBr(inst);
|
||||
|
||||
const reloc = try self.condBr(cond);
|
||||
|
||||
// If the condition dies here in this condbr instruction, process
|
||||
// that death now instead of later as this has an effect on
|
||||
@ -4466,6 +4516,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
|
||||
}
|
||||
|
||||
fn airTry(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = self.air.extraData(Air.Try, pl_op.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const result: MCValue = result: {
|
||||
const error_union_ty = self.air.typeOf(pl_op.operand);
|
||||
const error_union = try self.resolveInst(pl_op.operand);
|
||||
const is_err_result = try self.isErr(error_union_ty, error_union);
|
||||
const reloc = try self.condBr(is_err_result);
|
||||
|
||||
try self.genBody(body);
|
||||
|
||||
try self.performReloc(reloc);
|
||||
break :result try self.errUnionPayload(error_union, error_union_ty);
|
||||
};
|
||||
return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
_ = body;
|
||||
return self.fail("TODO implement airTryPtr for arm", .{});
|
||||
// return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
|
||||
}
|
||||
|
||||
fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
|
||||
// First section of indexes correspond to a set number of constant values.
|
||||
const ref_int = @enumToInt(inst);
|
||||
|
||||
@ -677,6 +677,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
.mul_add => try self.airMulAdd(inst),
|
||||
|
||||
.@"try" => try self.airTry(inst),
|
||||
.try_ptr => try self.airTryPtr(inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
@ -1834,8 +1837,8 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
|
||||
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
|
||||
const error_union_ty = self.air.typeOf(ty_op.operand);
|
||||
const mcv = try self.resolveInst(ty_op.operand);
|
||||
break :result try self.errUnionPayload(mcv, error_union_ty);
|
||||
const error_union = try self.resolveInst(ty_op.operand);
|
||||
break :result try self.errUnionPayload(error_union, error_union_ty);
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
@ -3702,6 +3705,42 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, .dead, .{ operand, .none, .none });
|
||||
}
|
||||
|
||||
/// Given a boolean condition, emit a jump that is taken when that
|
||||
/// condition is false.
|
||||
fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
|
||||
const condition_code: Condition = switch (condition) {
|
||||
.cpsr_flags => |cond| cond.negate(),
|
||||
else => blk: {
|
||||
const reg = switch (condition) {
|
||||
.register => |r| r,
|
||||
else => try self.copyToTmpRegister(Type.bool, condition),
|
||||
};
|
||||
|
||||
try self.spillCompareFlagsIfOccupied();
|
||||
|
||||
// cmp reg, 1
|
||||
// bne ...
|
||||
_ = try self.addInst(.{
|
||||
.tag = .cmp,
|
||||
.cond = .al,
|
||||
.data = .{ .rr_op = .{
|
||||
.rd = .r0,
|
||||
.rn = reg,
|
||||
.op = Instruction.Operand.imm(1, 0),
|
||||
} },
|
||||
});
|
||||
|
||||
break :blk .ne;
|
||||
},
|
||||
};
|
||||
|
||||
return try self.addInst(.{
|
||||
.tag = .b,
|
||||
.cond = condition_code,
|
||||
.data = .{ .inst = undefined }, // populated later through performReloc
|
||||
});
|
||||
}
|
||||
|
||||
fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const cond_inst = try self.resolveInst(pl_op.operand);
|
||||
@ -3710,39 +3749,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
|
||||
const liveness_condbr = self.liveness.getCondBr(inst);
|
||||
|
||||
const reloc: Mir.Inst.Index = reloc: {
|
||||
const condition: Condition = switch (cond_inst) {
|
||||
.cpsr_flags => |cond| cond.negate(),
|
||||
else => blk: {
|
||||
const reg = switch (cond_inst) {
|
||||
.register => |r| r,
|
||||
else => try self.copyToTmpRegister(Type.bool, cond_inst),
|
||||
};
|
||||
|
||||
try self.spillCompareFlagsIfOccupied();
|
||||
|
||||
// cmp reg, 1
|
||||
// bne ...
|
||||
_ = try self.addInst(.{
|
||||
.tag = .cmp,
|
||||
.cond = .al,
|
||||
.data = .{ .rr_op = .{
|
||||
.rd = .r0,
|
||||
.rn = reg,
|
||||
.op = Instruction.Operand.imm(1, 0),
|
||||
} },
|
||||
});
|
||||
|
||||
break :blk .ne;
|
||||
},
|
||||
};
|
||||
|
||||
break :reloc try self.addInst(.{
|
||||
.tag = .b,
|
||||
.cond = condition,
|
||||
.data = .{ .inst = undefined }, // populated later through performReloc
|
||||
});
|
||||
};
|
||||
const reloc: Mir.Inst.Index = try self.condBr(cond_inst);
|
||||
|
||||
// If the condition dies here in this condbr instruction, process
|
||||
// that death now instead of later as this has an effect on
|
||||
@ -4154,13 +4161,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
|
||||
.lhs = condition,
|
||||
.rhs = item,
|
||||
} };
|
||||
const cmp_result = try self.cmp(operands, condition_ty, .neq);
|
||||
|
||||
relocs[0] = try self.addInst(.{
|
||||
.tag = .b,
|
||||
.cond = cmp_result.cpsr_flags,
|
||||
.data = .{ .inst = undefined }, // populated later through performReloc
|
||||
});
|
||||
const cmp_result = try self.cmp(operands, condition_ty, .eq);
|
||||
relocs[0] = try self.condBr(cmp_result);
|
||||
} else {
|
||||
return self.fail("TODO switch with multiple items", .{});
|
||||
}
|
||||
@ -5145,6 +5147,33 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
|
||||
}
|
||||
|
||||
fn airTry(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = self.air.extraData(Air.Try, pl_op.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const result: MCValue = result: {
|
||||
const error_union_ty = self.air.typeOf(pl_op.operand);
|
||||
const error_union = try self.resolveInst(pl_op.operand);
|
||||
const is_err_result = try self.isErr(error_union_ty, error_union);
|
||||
const reloc = try self.condBr(is_err_result);
|
||||
|
||||
try self.genBody(body);
|
||||
|
||||
try self.performReloc(reloc);
|
||||
break :result try self.errUnionPayload(error_union, error_union_ty);
|
||||
};
|
||||
return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
_ = body;
|
||||
return self.fail("TODO implement airTryPtr for arm", .{});
|
||||
// return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
|
||||
}
|
||||
|
||||
fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
|
||||
// First section of indexes correspond to a set number of constant values.
|
||||
const ref_int = @enumToInt(inst);
|
||||
|
||||
@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
.mul_add => try self.airMulAdd(inst),
|
||||
|
||||
.@"try" => @panic("TODO"),
|
||||
.try_ptr => @panic("TODO"),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
|
||||
@ -604,6 +604,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => @panic("TODO try self.airPrefetch(inst)"),
|
||||
.mul_add => @panic("TODO try self.airMulAdd(inst)"),
|
||||
|
||||
.@"try" => @panic("TODO try self.airTry(inst)"),
|
||||
.try_ptr => @panic("TODO try self.airTryPtr(inst)"),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
|
||||
@ -1490,6 +1490,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
.int_to_float => self.airIntToFloat(inst),
|
||||
.get_union_tag => self.airGetUnionTag(inst),
|
||||
|
||||
.@"try" => self.airTry(inst),
|
||||
.try_ptr => self.airTryPtr(inst),
|
||||
|
||||
// TODO
|
||||
.dbg_inline_begin,
|
||||
.dbg_inline_end,
|
||||
@ -4623,3 +4626,68 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !WValue {
|
||||
} });
|
||||
return WValue{ .none = {} };
|
||||
}
|
||||
|
||||
fn airTry(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const err_union = try self.resolveInst(pl_op.operand);
|
||||
const extra = self.air.extraData(Air.Try, pl_op.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union_ty = self.air.typeOf(pl_op.operand);
|
||||
return lowerTry(self, err_union, body, err_union_ty, false);
|
||||
}
|
||||
|
||||
fn airTryPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
|
||||
const err_union_ptr = try self.resolveInst(extra.data.ptr);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
|
||||
return lowerTry(self, err_union_ptr, body, err_union_ty, true);
|
||||
}
|
||||
|
||||
fn lowerTry(
|
||||
self: *Self,
|
||||
err_union: WValue,
|
||||
body: []const Air.Inst.Index,
|
||||
err_union_ty: Type,
|
||||
operand_is_ptr: bool,
|
||||
) InnerError!WValue {
|
||||
if (operand_is_ptr) {
|
||||
return self.fail("TODO: lowerTry for pointers", .{});
|
||||
}
|
||||
|
||||
if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
|
||||
return err_union;
|
||||
}
|
||||
|
||||
const pl_ty = err_union_ty.errorUnionPayload();
|
||||
const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime();
|
||||
|
||||
// Block we can jump out of when error is not set
|
||||
try self.startBlock(.block, wasm.block_empty);
|
||||
|
||||
// check if the error tag is set for the error union.
|
||||
try self.emitWValue(err_union);
|
||||
if (pl_has_bits) {
|
||||
const err_offset = @intCast(u32, errUnionErrorOffset(pl_ty, self.target));
|
||||
try self.addMemArg(.i32_load16_u, .{
|
||||
.offset = err_union.offset() + err_offset,
|
||||
.alignment = Type.anyerror.abiAlignment(self.target),
|
||||
});
|
||||
}
|
||||
try self.addTag(.i32_eqz);
|
||||
try self.addLabel(.br_if, 0); // jump out of block when error is '0'
|
||||
try self.genBody(body);
|
||||
try self.endBlock();
|
||||
|
||||
// if we reach here it means error was not set, and we want the payload
|
||||
if (!pl_has_bits) {
|
||||
return WValue{ .none = {} };
|
||||
}
|
||||
|
||||
const pl_offset = @intCast(u32, errUnionPayloadOffset(pl_ty, self.target));
|
||||
if (isByRef(pl_ty, self.target)) {
|
||||
return buildPointerOffset(self, err_union, pl_offset, .new);
|
||||
}
|
||||
return self.load(err_union, pl_ty, pl_offset);
|
||||
}
|
||||
|
||||
@ -681,6 +681,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
|
||||
.prefetch => try self.airPrefetch(inst),
|
||||
.mul_add => try self.airMulAdd(inst),
|
||||
|
||||
.@"try" => try self.airTry(inst),
|
||||
.try_ptr => try self.airTryPtr(inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try self.airDbgVar(inst),
|
||||
@ -1804,14 +1807,24 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
const err_union_ty = self.air.typeOf(ty_op.operand);
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, operand);
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn genUnwrapErrorUnionPayloadMir(
|
||||
self: *Self,
|
||||
maybe_inst: ?Air.Inst.Index,
|
||||
err_union_ty: Type,
|
||||
err_union: MCValue,
|
||||
) !MCValue {
|
||||
const payload_ty = err_union_ty.errorUnionPayload();
|
||||
const err_ty = err_union_ty.errorUnionSet();
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
|
||||
const result: MCValue = result: {
|
||||
if (err_ty.errorSetCardinality() == .zero) {
|
||||
// TODO check if we can reuse
|
||||
break :result operand;
|
||||
break :result err_union;
|
||||
}
|
||||
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
@ -1819,7 +1832,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
|
||||
}
|
||||
|
||||
const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
|
||||
switch (operand) {
|
||||
switch (err_union) {
|
||||
.stack_offset => |off| {
|
||||
const offset = off - @intCast(i32, payload_off);
|
||||
break :result MCValue{ .stack_offset = offset };
|
||||
@ -1828,19 +1841,23 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
|
||||
// TODO reuse operand
|
||||
const lock = self.register_manager.lockRegAssumeUnused(reg);
|
||||
defer self.register_manager.unlockReg(lock);
|
||||
const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand);
|
||||
const result_reg: Register = if (maybe_inst) |inst|
|
||||
(try self.copyToRegisterWithInstTracking(inst, err_union_ty, err_union)).register
|
||||
else
|
||||
try self.copyToTmpRegister(err_union_ty, err_union);
|
||||
if (payload_off > 0) {
|
||||
const shift = @intCast(u6, payload_off * 8);
|
||||
try self.genShiftBinOpMir(.shr, err_union_ty, result.register, .{ .immediate = shift });
|
||||
try self.genShiftBinOpMir(.shr, err_union_ty, result_reg, .{ .immediate = shift });
|
||||
} else {
|
||||
try self.truncateRegister(payload_ty, result.register);
|
||||
try self.truncateRegister(payload_ty, result_reg);
|
||||
}
|
||||
break :result result;
|
||||
break :result MCValue{ .register = result_reg };
|
||||
},
|
||||
else => return self.fail("TODO implement unwrap_err_payload for {}", .{operand}),
|
||||
else => return self.fail("TODO implement genUnwrapErrorUnionPayloadMir for {}", .{err_union}),
|
||||
}
|
||||
};
|
||||
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// *(E!T) -> E
|
||||
@ -4228,6 +4245,45 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void {
|
||||
return self.finishAir(inst, result, .{ un_op, .none, .none });
|
||||
}
|
||||
|
||||
fn airTry(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = self.air.extraData(Air.Try, pl_op.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union_ty = self.air.typeOf(pl_op.operand);
|
||||
const err_union = try self.resolveInst(pl_op.operand);
|
||||
const result = try self.genTry(inst, err_union, body, err_union_ty, false);
|
||||
return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
|
||||
const err_union_ptr = try self.resolveInst(extra.data.ptr);
|
||||
const result = try self.genTry(inst, err_union_ptr, body, err_union_ty, true);
|
||||
return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none });
|
||||
}
|
||||
|
||||
fn genTry(
|
||||
self: *Self,
|
||||
inst: Air.Inst.Index,
|
||||
err_union: MCValue,
|
||||
body: []const Air.Inst.Index,
|
||||
err_union_ty: Type,
|
||||
operand_is_ptr: bool,
|
||||
) !MCValue {
|
||||
if (operand_is_ptr) {
|
||||
return self.fail("TODO genTry for pointers", .{});
|
||||
}
|
||||
const is_err_mcv = try self.isErr(null, err_union_ty, err_union);
|
||||
const reloc = try self.genCondBrMir(Type.anyerror, is_err_mcv);
|
||||
try self.genBody(body);
|
||||
try self.performReloc(reloc);
|
||||
const result = try self.genUnwrapErrorUnionPayloadMir(inst, err_union_ty, err_union);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
|
||||
const payload = try self.addExtra(Mir.DbgLineColumn{
|
||||
@ -4593,7 +4649,7 @@ fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCV
|
||||
return MCValue{ .eflags = is_null_res.eflags.negate() };
|
||||
}
|
||||
|
||||
fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
|
||||
fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) !MCValue {
|
||||
const err_type = ty.errorUnionSet();
|
||||
|
||||
if (err_type.errorSetCardinality() == .zero) {
|
||||
@ -4601,7 +4657,9 @@ fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue
|
||||
}
|
||||
|
||||
try self.spillEflagsIfOccupied();
|
||||
self.eflags_inst = inst;
|
||||
if (maybe_inst) |inst| {
|
||||
self.eflags_inst = inst;
|
||||
}
|
||||
|
||||
const err_off = errUnionErrorOffset(ty.errorUnionPayload(), self.target.*);
|
||||
switch (operand) {
|
||||
|
||||
@ -1875,6 +1875,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
||||
.union_init => try airUnionInit(f, inst),
|
||||
.prefetch => try airPrefetch(f, inst),
|
||||
|
||||
.@"try" => try airTry(f, inst),
|
||||
.try_ptr => try airTryPtr(f, inst),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try airDbgVar(f, inst),
|
||||
@ -2861,6 +2864,91 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
const pl_op = f.air.instructions.items(.data)[inst].pl_op;
|
||||
const err_union = try f.resolveInst(pl_op.operand);
|
||||
const extra = f.air.extraData(Air.Try, pl_op.payload);
|
||||
const body = f.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union_ty = f.air.typeOf(pl_op.operand);
|
||||
const result_ty = f.air.typeOfIndex(inst);
|
||||
return lowerTry(f, err_union, body, err_union_ty, false, result_ty);
|
||||
}
|
||||
|
||||
fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = f.air.extraData(Air.TryPtr, ty_pl.payload);
|
||||
const err_union_ptr = try f.resolveInst(extra.data.ptr);
|
||||
const body = f.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union_ty = f.air.typeOf(extra.data.ptr).childType();
|
||||
const result_ty = f.air.typeOfIndex(inst);
|
||||
return lowerTry(f, err_union_ptr, body, err_union_ty, true, result_ty);
|
||||
}
|
||||
|
||||
fn lowerTry(
|
||||
f: *Function,
|
||||
err_union: CValue,
|
||||
body: []const Air.Inst.Index,
|
||||
err_union_ty: Type,
|
||||
operand_is_ptr: bool,
|
||||
result_ty: Type,
|
||||
) !CValue {
|
||||
if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
|
||||
// If the error set has no fields, then the payload and the error
|
||||
// union are the same value.
|
||||
return err_union;
|
||||
}
|
||||
|
||||
const payload_ty = err_union_ty.errorUnionPayload();
|
||||
const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
|
||||
|
||||
const writer = f.object.writer();
|
||||
|
||||
err: {
|
||||
if (!payload_has_bits) {
|
||||
if (operand_is_ptr) {
|
||||
try writer.writeAll("if(*");
|
||||
} else {
|
||||
try writer.writeAll("if(");
|
||||
}
|
||||
try f.writeCValue(writer, err_union);
|
||||
try writer.writeAll(")");
|
||||
break :err;
|
||||
}
|
||||
if (operand_is_ptr or isByRef(err_union_ty)) {
|
||||
try writer.writeAll("if(");
|
||||
try f.writeCValue(writer, err_union);
|
||||
try writer.writeAll("->error)");
|
||||
break :err;
|
||||
}
|
||||
try writer.writeAll("if(");
|
||||
try f.writeCValue(writer, err_union);
|
||||
try writer.writeAll(".error)");
|
||||
}
|
||||
|
||||
try genBody(f, body);
|
||||
try f.object.indent_writer.insertNewline();
|
||||
|
||||
if (!payload_has_bits) {
|
||||
if (!operand_is_ptr) {
|
||||
return CValue.none;
|
||||
} else {
|
||||
return err_union;
|
||||
}
|
||||
}
|
||||
|
||||
const local = try f.allocLocal(result_ty, .Const);
|
||||
if (operand_is_ptr or isByRef(payload_ty)) {
|
||||
try writer.writeAll(" = &");
|
||||
try f.writeCValue(writer, err_union);
|
||||
try writer.writeAll("->payload;\n");
|
||||
} else {
|
||||
try writer.writeAll(" = ");
|
||||
try f.writeCValue(writer, err_union);
|
||||
try writer.writeAll(".payload;\n");
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
fn airBr(f: *Function, inst: Air.Inst.Index) !CValue {
|
||||
const branch = f.air.instructions.items(.data)[inst].br;
|
||||
const block = f.blocks.get(branch.block_inst).?;
|
||||
@ -4221,3 +4309,8 @@ fn loweredFnRetTyHasBits(fn_ty: Type) bool {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn isByRef(ty: Type) bool {
|
||||
_ = ty;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4040,6 +4040,8 @@ pub const FuncGen = struct {
|
||||
.ret_addr => try self.airRetAddr(inst),
|
||||
.frame_addr => try self.airFrameAddress(inst),
|
||||
.cond_br => try self.airCondBr(inst),
|
||||
.@"try" => try self.airTry(inst),
|
||||
.try_ptr => try self.airTryPtr(inst),
|
||||
.intcast => try self.airIntCast(inst),
|
||||
.trunc => try self.airTrunc(inst),
|
||||
.fptrunc => try self.airFptrunc(inst),
|
||||
@ -4731,6 +4733,75 @@ pub const FuncGen = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
fn airTry(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const err_union = try self.resolveInst(pl_op.operand);
|
||||
const extra = self.air.extraData(Air.Try, pl_op.payload);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union_ty = self.air.typeOf(pl_op.operand);
|
||||
const result_ty = self.air.typeOfIndex(inst);
|
||||
return lowerTry(self, err_union, body, err_union_ty, false, result_ty);
|
||||
}
|
||||
|
||||
fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = self.air.extraData(Air.TryPtr, ty_pl.payload);
|
||||
const err_union_ptr = try self.resolveInst(extra.data.ptr);
|
||||
const body = self.air.extra[extra.end..][0..extra.data.body_len];
|
||||
const err_union_ty = self.air.typeOf(extra.data.ptr).childType();
|
||||
const result_ty = self.air.typeOfIndex(inst);
|
||||
return lowerTry(self, err_union_ptr, body, err_union_ty, true, result_ty);
|
||||
}
|
||||
|
||||
fn lowerTry(fg: *FuncGen, err_union: *const llvm.Value, body: []const Air.Inst.Index, err_union_ty: Type, operand_is_ptr: bool, result_ty: Type) !?*const llvm.Value {
|
||||
if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
|
||||
// If the error set has no fields, then the payload and the error
|
||||
// union are the same value.
|
||||
return err_union;
|
||||
}
|
||||
|
||||
const payload_ty = err_union_ty.errorUnionPayload();
|
||||
const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime();
|
||||
const target = fg.dg.module.getTarget();
|
||||
const is_err = err: {
|
||||
const err_set_ty = try fg.dg.lowerType(Type.anyerror);
|
||||
const zero = err_set_ty.constNull();
|
||||
if (!payload_has_bits) {
|
||||
const loaded = if (operand_is_ptr) fg.builder.buildLoad(err_union, "") else err_union;
|
||||
break :err fg.builder.buildICmp(.NE, loaded, zero, "");
|
||||
}
|
||||
const err_field_index = errUnionErrorOffset(payload_ty, target);
|
||||
if (operand_is_ptr or isByRef(err_union_ty)) {
|
||||
const err_field_ptr = fg.builder.buildStructGEP(err_union, err_field_index, "");
|
||||
const loaded = fg.builder.buildLoad(err_field_ptr, "");
|
||||
break :err fg.builder.buildICmp(.NE, loaded, zero, "");
|
||||
}
|
||||
const loaded = fg.builder.buildExtractValue(err_union, err_field_index, "");
|
||||
break :err fg.builder.buildICmp(.NE, loaded, zero, "");
|
||||
};
|
||||
|
||||
const return_block = fg.context.appendBasicBlock(fg.llvm_func, "TryRet");
|
||||
const continue_block = fg.context.appendBasicBlock(fg.llvm_func, "TryCont");
|
||||
_ = fg.builder.buildCondBr(is_err, return_block, continue_block);
|
||||
|
||||
fg.builder.positionBuilderAtEnd(return_block);
|
||||
try fg.genBody(body);
|
||||
|
||||
fg.builder.positionBuilderAtEnd(continue_block);
|
||||
if (!payload_has_bits) {
|
||||
if (!operand_is_ptr) return null;
|
||||
|
||||
// TODO once we update to LLVM 14 this bitcast won't be necessary.
|
||||
const res_ptr_ty = try fg.dg.lowerType(result_ty);
|
||||
return fg.builder.buildBitCast(err_union, res_ptr_ty, "");
|
||||
}
|
||||
const offset = errUnionPayloadOffset(payload_ty, target);
|
||||
if (operand_is_ptr or isByRef(payload_ty)) {
|
||||
return fg.builder.buildStructGEP(err_union, offset, "");
|
||||
}
|
||||
return fg.builder.buildExtractValue(err_union, offset, "");
|
||||
}
|
||||
|
||||
fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
|
||||
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
|
||||
const cond = try self.resolveInst(pl_op.operand);
|
||||
@ -5673,15 +5744,14 @@ pub const FuncGen = struct {
|
||||
const operand = try self.resolveInst(ty_op.operand);
|
||||
const operand_ty = self.air.typeOf(ty_op.operand);
|
||||
const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
|
||||
// If the error set has no fields, then the payload and the error
|
||||
// union are the same value.
|
||||
if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
|
||||
// If the error set has no fields, then the payload and the error
|
||||
// union are the same value.
|
||||
return operand;
|
||||
}
|
||||
const result_ty = self.air.typeOfIndex(inst);
|
||||
const payload_ty = if (operand_is_ptr) result_ty.childType() else result_ty;
|
||||
const target = self.dg.module.getTarget();
|
||||
const offset = errUnionPayloadOffset(payload_ty, target);
|
||||
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
if (!operand_is_ptr) return null;
|
||||
@ -5690,6 +5760,7 @@ pub const FuncGen = struct {
|
||||
const res_ptr_ty = try self.dg.lowerType(result_ty);
|
||||
return self.builder.buildBitCast(operand, res_ptr_ty, "");
|
||||
}
|
||||
const offset = errUnionPayloadOffset(payload_ty, target);
|
||||
if (operand_is_ptr or isByRef(payload_ty)) {
|
||||
return self.builder.buildStructGEP(operand, offset, "");
|
||||
}
|
||||
|
||||
@ -258,6 +258,8 @@ const Writer = struct {
|
||||
.union_init => try w.writeUnionInit(s, inst),
|
||||
.br => try w.writeBr(s, inst),
|
||||
.cond_br => try w.writeCondBr(s, inst),
|
||||
.@"try" => try w.writeTry(s, inst),
|
||||
.try_ptr => try w.writeTryPtr(s, inst),
|
||||
.switch_br => try w.writeSwitchBr(s, inst),
|
||||
.cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
|
||||
.fence => try w.writeFence(s, inst),
|
||||
@ -624,6 +626,36 @@ const Writer = struct {
|
||||
try w.writeOperand(s, inst, 0, br.operand);
|
||||
}
|
||||
|
||||
fn writeTry(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = w.air.extraData(Air.Try, pl_op.payload);
|
||||
const body = w.air.extra[extra.end..][0..extra.data.body_len];
|
||||
|
||||
try w.writeOperand(s, inst, 0, pl_op.operand);
|
||||
try s.writeAll(", {\n");
|
||||
const old_indent = w.indent;
|
||||
w.indent += 2;
|
||||
try w.writeBody(s, body);
|
||||
w.indent = old_indent;
|
||||
try s.writeByteNTimes(' ', w.indent);
|
||||
try s.writeAll("}");
|
||||
}
|
||||
|
||||
fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
|
||||
const extra = w.air.extraData(Air.TryPtr, ty_pl.payload);
|
||||
const body = w.air.extra[extra.end..][0..extra.data.body_len];
|
||||
|
||||
try w.writeOperand(s, inst, 0, extra.data.ptr);
|
||||
try s.print(", {}, {{\n", .{w.air.getRefType(ty_pl.ty).fmtDebug()});
|
||||
const old_indent = w.indent;
|
||||
w.indent += 2;
|
||||
try w.writeBody(s, body);
|
||||
w.indent = old_indent;
|
||||
try s.writeByteNTimes(' ', w.indent);
|
||||
try s.writeAll("}");
|
||||
}
|
||||
|
||||
fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
|
||||
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
|
||||
const extra = w.air.extraData(Air.CondBr, pl_op.payload);
|
||||
|
||||
@ -374,17 +374,21 @@ const Writer = struct {
|
||||
.validate_array_init_comptime,
|
||||
.c_import,
|
||||
.typeof_builtin,
|
||||
=> try self.writePlNodeBlock(stream, inst),
|
||||
=> try self.writeBlock(stream, inst),
|
||||
|
||||
.condbr,
|
||||
.condbr_inline,
|
||||
=> try self.writePlNodeCondBr(stream, inst),
|
||||
=> try self.writeCondBr(stream, inst),
|
||||
|
||||
.@"try",
|
||||
.try_ptr,
|
||||
=> try self.writeTry(stream, inst),
|
||||
|
||||
.error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent),
|
||||
.error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
|
||||
.error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
|
||||
|
||||
.switch_block => try self.writePlNodeSwitchBlock(stream, inst),
|
||||
.switch_block => try self.writeSwitchBlock(stream, inst),
|
||||
|
||||
.field_ptr,
|
||||
.field_val,
|
||||
@ -1171,7 +1175,7 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
fn writeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
try self.writePlNodeBlockWithoutSrc(stream, inst);
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
@ -1185,7 +1189,7 @@ const Writer = struct {
|
||||
try stream.writeAll(") ");
|
||||
}
|
||||
|
||||
fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
fn writeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
|
||||
const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];
|
||||
@ -1199,6 +1203,17 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeTry(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.Try, inst_data.payload_index);
|
||||
const body = self.code.extra[extra.end..][0..extra.data.body_len];
|
||||
try self.writeInstRef(stream, extra.data.operand);
|
||||
try stream.writeAll(", ");
|
||||
try self.writeBracedBody(stream, body);
|
||||
try stream.writeAll(") ");
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
|
||||
const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
|
||||
|
||||
@ -1746,7 +1761,7 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user