authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-17 16:51:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-17 23:50:38-07:00
log9031cc54f27b4483c62ab904dd94063a9499c842
tree0c771125a45adab3a9d80cbc6b101cad2bed0fd1
parent691fba38d8cf28a32e6d4a4cd2111c401e763d08

Sema: implement `@intCast` for vectors


2 files changed, 75 insertions(+), 26 deletions(-)

src/Sema.zig+70-25
...@@ -7020,22 +7020,25 @@ fn intCast(...@@ -7020,22 +7020,25 @@ fn intCast(
7020 operand_src: LazySrcLoc,7020 operand_src: LazySrcLoc,
7021 runtime_safety: bool,7021 runtime_safety: bool,
7022) CompileError!Air.Inst.Ref {7022) CompileError!Air.Inst.Ref {
7023 // TODO: Add support for vectors7023 const operand_ty = sema.typeOf(operand);
7024 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);7024 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src);
7025 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));7025 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
70267026
7027 if (try sema.isComptimeKnown(block, operand_src, operand)) {7027 if (try sema.isComptimeKnown(block, operand_src, operand)) {
7028 return sema.coerce(block, dest_ty, operand, operand_src);7028 return sema.coerce(block, dest_ty, operand, operand_src);
7029 } else if (dest_is_comptime_int) {7029 } else if (dest_scalar_ty.zigTypeTag() == .ComptimeInt) {
7030 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});7030 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
7031 }7031 }
70327032
7033 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
7034 const is_vector = dest_ty.zigTypeTag() == .Vector;
7035
7033 if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| {7036 if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| {
7034 // requirement: intCast(u0, input) iff input == 07037 // requirement: intCast(u0, input) iff input == 0
7035 if (runtime_safety and block.wantSafety()) {7038 if (runtime_safety and block.wantSafety()) {
7036 try sema.requireRuntimeBlock(block, operand_src);7039 try sema.requireRuntimeBlock(block, operand_src);
7037 const target = sema.mod.getTarget();7040 const target = sema.mod.getTarget();
7038 const wanted_info = dest_ty.intInfo(target);7041 const wanted_info = dest_scalar_ty.intInfo(target);
7039 const wanted_bits = wanted_info.bits;7042 const wanted_bits = wanted_info.bits;
70407043
7041 if (wanted_bits == 0) {7044 if (wanted_bits == 0) {
...@@ -7051,9 +7054,8 @@ fn intCast(...@@ -7051,9 +7054,8 @@ fn intCast(
7051 try sema.requireRuntimeBlock(block, operand_src);7054 try sema.requireRuntimeBlock(block, operand_src);
7052 if (runtime_safety and block.wantSafety()) {7055 if (runtime_safety and block.wantSafety()) {
7053 const target = sema.mod.getTarget();7056 const target = sema.mod.getTarget();
7054 const operand_ty = sema.typeOf(operand);7057 const actual_info = operand_scalar_ty.intInfo(target);
7055 const actual_info = operand_ty.intInfo(target);7058 const wanted_info = dest_scalar_ty.intInfo(target);
7056 const wanted_info = dest_ty.intInfo(target);
7057 const actual_bits = actual_info.bits;7059 const actual_bits = actual_info.bits;
7058 const wanted_bits = wanted_info.bits;7060 const wanted_bits = wanted_info.bits;
7059 const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed);7061 const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed);
...@@ -7062,7 +7064,11 @@ fn intCast(...@@ -7062,7 +7064,11 @@ fn intCast(
7062 // range shrinkage7064 // range shrinkage
7063 // requirement: int value fits into target type7065 // requirement: int value fits into target type
7064 if (wanted_value_bits < actual_value_bits) {7066 if (wanted_value_bits < actual_value_bits) {
7065 const dest_max_val = try dest_ty.maxInt(sema.arena, target);7067 const dest_max_val_scalar = try dest_scalar_ty.maxInt(sema.arena, target);
7068 const dest_max_val = if (is_vector)
7069 try Value.Tag.repeated.create(sema.arena, dest_max_val_scalar)
7070 else
7071 dest_max_val_scalar;
7066 const dest_max = try sema.addConstant(operand_ty, dest_max_val);7072 const dest_max = try sema.addConstant(operand_ty, dest_max_val);
7067 const diff = try block.addBinOp(.subwrap, dest_max, operand);7073 const diff = try block.addBinOp(.subwrap, dest_max, operand);
70687074
...@@ -7080,19 +7086,59 @@ fn intCast(...@@ -7080,19 +7086,59 @@ fn intCast(
7080 } else dest_max_val;7086 } else dest_max_val;
7081 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);7087 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);
70827088
7083 const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);7089 const ok = if (is_vector) ok: {
7084 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);7090 const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte, try sema.addType(operand_ty));
7091 const all_in_range = try block.addInst(.{
7092 .tag = .reduce,
7093 .data = .{ .reduce = .{
7094 .operand = is_in_range,
7095 .operation = .And,
7096 } },
7097 });
7098 break :ok all_in_range;
7099 } else ok: {
7100 const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);
7101 break :ok is_in_range;
7102 };
7103 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
7085 } else {7104 } else {
7086 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);7105 const ok = if (is_vector) ok: {
7087 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);7106 const is_in_range = try block.addCmpVector(diff, dest_max, .lte, try sema.addType(operand_ty));
7107 const all_in_range = try block.addInst(.{
7108 .tag = .reduce,
7109 .data = .{ .reduce = .{
7110 .operand = is_in_range,
7111 .operation = .And,
7112 } },
7113 });
7114 break :ok all_in_range;
7115 } else ok: {
7116 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
7117 break :ok is_in_range;
7118 };
7119 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
7088 }7120 }
7089 }7121 } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
7090 // no shrinkage, yes sign loss7122 // no shrinkage, yes sign loss
7091 // requirement: signed to unsigned >= 07123 // requirement: signed to unsigned >= 0
7092 else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {7124 const ok = if (is_vector) ok: {
7093 const zero_inst = try sema.addConstant(operand_ty, Value.zero);7125 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
7094 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);7126 const zero_inst = try sema.addConstant(operand_ty, zero_val);
7095 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);7127 const is_in_range = try block.addCmpVector(operand, zero_inst, .lte, try sema.addType(operand_ty));
7128 const all_in_range = try block.addInst(.{
7129 .tag = .reduce,
7130 .data = .{ .reduce = .{
7131 .operand = is_in_range,
7132 .operation = .And,
7133 } },
7134 });
7135 break :ok all_in_range;
7136 } else ok: {
7137 const zero_inst = try sema.addConstant(operand_ty, Value.zero);
7138 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
7139 break :ok is_in_range;
7140 };
7141 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
7096 }7142 }
7097 }7143 }
7098 return block.addTyOp(.intcast, dest_ty, operand);7144 return block.addTyOp(.intcast, dest_ty, operand);
...@@ -14517,8 +14563,8 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -14517,8 +14563,8 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
14517 const dest_scalar_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);14563 const dest_scalar_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
14518 const operand = sema.resolveInst(extra.rhs);14564 const operand = sema.resolveInst(extra.rhs);
14519 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_scalar_ty);14565 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_scalar_ty);
14520 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src);
14521 const operand_ty = sema.typeOf(operand);14566 const operand_ty = sema.typeOf(operand);
14567 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
14522 const is_vector = operand_ty.zigTypeTag() == .Vector;14568 const is_vector = operand_ty.zigTypeTag() == .Vector;
14523 const dest_ty = if (is_vector)14569 const dest_ty = if (is_vector)
14524 try Type.vector(sema.arena, operand_ty.vectorLen(), dest_scalar_ty)14570 try Type.vector(sema.arena, operand_ty.vectorLen(), dest_scalar_ty)
...@@ -14686,7 +14732,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -14686,7 +14732,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
14686 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };14732 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
14687 const operand = sema.resolveInst(inst_data.operand);14733 const operand = sema.resolveInst(inst_data.operand);
14688 const operand_ty = sema.typeOf(operand);14734 const operand_ty = sema.typeOf(operand);
14689 const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src);14735 const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
14690 const target = sema.mod.getTarget();14736 const target = sema.mod.getTarget();
14691 const bits = scalar_ty.intInfo(target).bits;14737 const bits = scalar_ty.intInfo(target).bits;
14692 if (bits % 8 != 0) {14738 if (bits % 8 != 0) {
...@@ -14743,7 +14789,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -14743,7 +14789,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
14743 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };14789 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
14744 const operand = sema.resolveInst(inst_data.operand);14790 const operand = sema.resolveInst(inst_data.operand);
14745 const operand_ty = sema.typeOf(operand);14791 const operand_ty = sema.typeOf(operand);
14746 _ = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src);14792 _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
1474714793
14748 if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| {14794 if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| {
14749 return sema.addConstant(operand_ty, val);14795 return sema.addConstant(operand_ty, val);
...@@ -15095,10 +15141,9 @@ fn checkIntOrVector(...@@ -15095,10 +15141,9 @@ fn checkIntOrVector(
15095fn checkIntOrVectorAllowComptime(15141fn checkIntOrVectorAllowComptime(
15096 sema: *Sema,15142 sema: *Sema,
15097 block: *Block,15143 block: *Block,
15098 operand: Air.Inst.Ref,15144 operand_ty: Type,
15099 operand_src: LazySrcLoc,15145 operand_src: LazySrcLoc,
15100) CompileError!Type {15146) CompileError!Type {
15101 const operand_ty = sema.typeOf(operand);
15102 switch (try operand_ty.zigTypeTagOrPoison()) {15147 switch (try operand_ty.zigTypeTagOrPoison()) {
15103 .Int, .ComptimeInt => return operand_ty,15148 .Int, .ComptimeInt => return operand_ty,
15104 .Vector => {15149 .Vector => {
test/behavior/cast.zig+5-1
...@@ -587,7 +587,11 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {...@@ -587,7 +587,11 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
587}587}
588588
589test "vector casts" {589test "vector casts" {
590 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO590 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
591 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
592 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
593 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
594 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
591595
592 const S = struct {596 const S = struct {
593 fn doTheTest() !void {597 fn doTheTest() !void {