authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-01 16:13:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-01 16:13:58-07:00
logddf14323ea9b2c75ac5ed286525d27730a192b53
treea8b586e69bef580747553f63da828f947f28aa21
parent6ae0825e7f87fc9b73a4b968964196b0e164f062

stage2: implement `@truncate`


12 files changed, 380 insertions(+), 279 deletions(-)

src/Air.zig+9-2
......@@ -206,10 +206,16 @@ pub const Inst = struct {
206206 /// Convert from one float type to another.
207207 /// Uses the `ty_op` field.
208208 floatcast,
209 /// TODO audit uses of this. We should have explicit instructions for integer
210 /// widening and truncating.
209 /// Returns an integer with a different type than the operand. The new type may have
210 /// fewer, the same, or more bits than the operand type. However, the instruction
211 /// guarantees that the same integer value fits in both types.
212 /// See `trunc` for integer truncation.
211213 /// Uses the `ty_op` field.
212214 intcast,
215 /// Truncate higher bits from an integer, resulting in an integer with the same
216 /// sign but an equal or smaller number of bits.
217 /// Uses the `ty_op` field.
218 trunc,
213219 /// ?T => T. If the value is null, undefined behavior.
214220 /// Uses the `ty_op` field.
215221 optional_payload,
......@@ -452,6 +458,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
452458 .load,
453459 .floatcast,
454460 .intcast,
461 .trunc,
455462 .optional_payload,
456463 .optional_payload_ptr,
457464 .wrap_optional,
src/Liveness.zig+1
......@@ -264,6 +264,7 @@ fn analyzeInst(
264264 .load,
265265 .floatcast,
266266 .intcast,
267 .trunc,
267268 .optional_payload,
268269 .optional_payload_ptr,
269270 .wrap_optional,
src/Module.zig-238
......@@ -4033,244 +4033,6 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) Co
40334033 return error.AnalysisFail;
40344034}
40354035
4036pub fn intAdd(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
4037 // TODO is this a performance issue? maybe we should try the operation without
4038 // resorting to BigInt first.
4039 var lhs_space: Value.BigIntSpace = undefined;
4040 var rhs_space: Value.BigIntSpace = undefined;
4041 const lhs_bigint = lhs.toBigInt(&lhs_space);
4042 const rhs_bigint = rhs.toBigInt(&rhs_space);
4043 const limbs = try allocator.alloc(
4044 std.math.big.Limb,
4045 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
4046 );
4047 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
4048 result_bigint.add(lhs_bigint, rhs_bigint);
4049 const result_limbs = result_bigint.limbs[0..result_bigint.len];
4050
4051 if (result_bigint.positive) {
4052 return Value.Tag.int_big_positive.create(allocator, result_limbs);
4053 } else {
4054 return Value.Tag.int_big_negative.create(allocator, result_limbs);
4055 }
4056}
4057
4058pub fn intSub(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
4059 // TODO is this a performance issue? maybe we should try the operation without
4060 // resorting to BigInt first.
4061 var lhs_space: Value.BigIntSpace = undefined;
4062 var rhs_space: Value.BigIntSpace = undefined;
4063 const lhs_bigint = lhs.toBigInt(&lhs_space);
4064 const rhs_bigint = rhs.toBigInt(&rhs_space);
4065 const limbs = try allocator.alloc(
4066 std.math.big.Limb,
4067 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
4068 );
4069 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
4070 result_bigint.sub(lhs_bigint, rhs_bigint);
4071 const result_limbs = result_bigint.limbs[0..result_bigint.len];
4072
4073 if (result_bigint.positive) {
4074 return Value.Tag.int_big_positive.create(allocator, result_limbs);
4075 } else {
4076 return Value.Tag.int_big_negative.create(allocator, result_limbs);
4077 }
4078}
4079
4080pub fn intDiv(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
4081 // TODO is this a performance issue? maybe we should try the operation without
4082 // resorting to BigInt first.
4083 var lhs_space: Value.BigIntSpace = undefined;
4084 var rhs_space: Value.BigIntSpace = undefined;
4085 const lhs_bigint = lhs.toBigInt(&lhs_space);
4086 const rhs_bigint = rhs.toBigInt(&rhs_space);
4087 const limbs_q = try allocator.alloc(
4088 std.math.big.Limb,
4089 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
4090 );
4091 const limbs_r = try allocator.alloc(
4092 std.math.big.Limb,
4093 lhs_bigint.limbs.len,
4094 );
4095 const limbs_buffer = try allocator.alloc(
4096 std.math.big.Limb,
4097 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
4098 );
4099 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
4100 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
4101 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
4102 const result_limbs = result_q.limbs[0..result_q.len];
4103
4104 if (result_q.positive) {
4105 return Value.Tag.int_big_positive.create(allocator, result_limbs);
4106 } else {
4107 return Value.Tag.int_big_negative.create(allocator, result_limbs);
4108 }
4109}
4110
4111pub fn intMul(allocator: *Allocator, lhs: Value, rhs: Value) !Value {
4112 // TODO is this a performance issue? maybe we should try the operation without
4113 // resorting to BigInt first.
4114 var lhs_space: Value.BigIntSpace = undefined;
4115 var rhs_space: Value.BigIntSpace = undefined;
4116 const lhs_bigint = lhs.toBigInt(&lhs_space);
4117 const rhs_bigint = rhs.toBigInt(&rhs_space);
4118 const limbs = try allocator.alloc(
4119 std.math.big.Limb,
4120 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
4121 );
4122 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
4123 var limbs_buffer = try allocator.alloc(
4124 std.math.big.Limb,
4125 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
4126 );
4127 defer allocator.free(limbs_buffer);
4128 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
4129 const result_limbs = result_bigint.limbs[0..result_bigint.len];
4130
4131 if (result_bigint.positive) {
4132 return Value.Tag.int_big_positive.create(allocator, result_limbs);
4133 } else {
4134 return Value.Tag.int_big_negative.create(allocator, result_limbs);
4135 }
4136}
4137
4138pub fn floatAdd(
4139 arena: *Allocator,
4140 float_type: Type,
4141 src: LazySrcLoc,
4142 lhs: Value,
4143 rhs: Value,
4144) !Value {
4145 _ = src;
4146 switch (float_type.tag()) {
4147 .f16 => {
4148 @panic("TODO add __trunctfhf2 to compiler-rt");
4149 //const lhs_val = lhs.toFloat(f16);
4150 //const rhs_val = rhs.toFloat(f16);
4151 //return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
4152 },
4153 .f32 => {
4154 const lhs_val = lhs.toFloat(f32);
4155 const rhs_val = rhs.toFloat(f32);
4156 return Value.Tag.float_32.create(arena, lhs_val + rhs_val);
4157 },
4158 .f64 => {
4159 const lhs_val = lhs.toFloat(f64);
4160 const rhs_val = rhs.toFloat(f64);
4161 return Value.Tag.float_64.create(arena, lhs_val + rhs_val);
4162 },
4163 .f128, .comptime_float, .c_longdouble => {
4164 const lhs_val = lhs.toFloat(f128);
4165 const rhs_val = rhs.toFloat(f128);
4166 return Value.Tag.float_128.create(arena, lhs_val + rhs_val);
4167 },
4168 else => unreachable,
4169 }
4170}
4171
4172pub fn floatSub(
4173 arena: *Allocator,
4174 float_type: Type,
4175 src: LazySrcLoc,
4176 lhs: Value,
4177 rhs: Value,
4178) !Value {
4179 _ = src;
4180 switch (float_type.tag()) {
4181 .f16 => {
4182 @panic("TODO add __trunctfhf2 to compiler-rt");
4183 //const lhs_val = lhs.toFloat(f16);
4184 //const rhs_val = rhs.toFloat(f16);
4185 //return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
4186 },
4187 .f32 => {
4188 const lhs_val = lhs.toFloat(f32);
4189 const rhs_val = rhs.toFloat(f32);
4190 return Value.Tag.float_32.create(arena, lhs_val - rhs_val);
4191 },
4192 .f64 => {
4193 const lhs_val = lhs.toFloat(f64);
4194 const rhs_val = rhs.toFloat(f64);
4195 return Value.Tag.float_64.create(arena, lhs_val - rhs_val);
4196 },
4197 .f128, .comptime_float, .c_longdouble => {
4198 const lhs_val = lhs.toFloat(f128);
4199 const rhs_val = rhs.toFloat(f128);
4200 return Value.Tag.float_128.create(arena, lhs_val - rhs_val);
4201 },
4202 else => unreachable,
4203 }
4204}
4205
4206pub fn floatDiv(
4207 arena: *Allocator,
4208 float_type: Type,
4209 src: LazySrcLoc,
4210 lhs: Value,
4211 rhs: Value,
4212) !Value {
4213 _ = src;
4214 switch (float_type.tag()) {
4215 .f16 => {
4216 @panic("TODO add __trunctfhf2 to compiler-rt");
4217 //const lhs_val = lhs.toFloat(f16);
4218 //const rhs_val = rhs.toFloat(f16);
4219 //return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
4220 },
4221 .f32 => {
4222 const lhs_val = lhs.toFloat(f32);
4223 const rhs_val = rhs.toFloat(f32);
4224 return Value.Tag.float_32.create(arena, lhs_val / rhs_val);
4225 },
4226 .f64 => {
4227 const lhs_val = lhs.toFloat(f64);
4228 const rhs_val = rhs.toFloat(f64);
4229 return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
4230 },
4231 .f128, .comptime_float, .c_longdouble => {
4232 const lhs_val = lhs.toFloat(f128);
4233 const rhs_val = rhs.toFloat(f128);
4234 return Value.Tag.float_128.create(arena, lhs_val / rhs_val);
4235 },
4236 else => unreachable,
4237 }
4238}
4239
4240pub fn floatMul(
4241 arena: *Allocator,
4242 float_type: Type,
4243 src: LazySrcLoc,
4244 lhs: Value,
4245 rhs: Value,
4246) !Value {
4247 _ = src;
4248 switch (float_type.tag()) {
4249 .f16 => {
4250 @panic("TODO add __trunctfhf2 to compiler-rt");
4251 //const lhs_val = lhs.toFloat(f16);
4252 //const rhs_val = rhs.toFloat(f16);
4253 //return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
4254 },
4255 .f32 => {
4256 const lhs_val = lhs.toFloat(f32);
4257 const rhs_val = rhs.toFloat(f32);
4258 return Value.Tag.float_32.create(arena, lhs_val * rhs_val);
4259 },
4260 .f64 => {
4261 const lhs_val = lhs.toFloat(f64);
4262 const rhs_val = rhs.toFloat(f64);
4263 return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
4264 },
4265 .f128, .comptime_float, .c_longdouble => {
4266 const lhs_val = lhs.toFloat(f128);
4267 const rhs_val = rhs.toFloat(f128);
4268 return Value.Tag.float_128.create(arena, lhs_val * rhs_val);
4269 },
4270 else => unreachable,
4271 }
4272}
4273
42744036pub fn simplePtrType(
42754037 arena: *Allocator,
42764038 elem_ty: Type,
src/Sema.zig+74-30
......@@ -3479,27 +3479,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
34793479 const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
34803480 const operand = sema.resolveInst(extra.rhs);
34813481
3482 const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
3483 .ComptimeInt => true,
3484 .Int => false,
3485 else => return sema.mod.fail(
3486 &block.base,
3487 dest_ty_src,
3488 "expected integer type, found '{}'",
3489 .{dest_type},
3490 ),
3491 };
3492
3493 const operand_ty = sema.typeOf(operand);
3494 switch (operand_ty.zigTypeTag()) {
3495 .ComptimeInt, .Int => {},
3496 else => return sema.mod.fail(
3497 &block.base,
3498 operand_src,
3499 "expected integer type, found '{}'",
3500 .{operand_ty},
3501 ),
3502 }
3482 const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_type);
3483 _ = try sema.requireIntegerType(block, operand_src, sema.typeOf(operand));
35033484
35043485 if (try sema.isComptimeKnown(block, operand_src, operand)) {
35053486 return sema.coerce(block, dest_type, operand, operand_src);
......@@ -4951,30 +4932,30 @@ fn analyzeArithmetic(
49514932 const value = switch (zir_tag) {
49524933 .add => blk: {
49534934 const val = if (is_int)
4954 try Module.intAdd(sema.arena, lhs_val, rhs_val)
4935 try lhs_val.intAdd(rhs_val, sema.arena)
49554936 else
4956 try Module.floatAdd(sema.arena, scalar_type, src, lhs_val, rhs_val);
4937 try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena);
49574938 break :blk val;
49584939 },
49594940 .sub => blk: {
49604941 const val = if (is_int)
4961 try Module.intSub(sema.arena, lhs_val, rhs_val)
4942 try lhs_val.intSub(rhs_val, sema.arena)
49624943 else
4963 try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val);
4944 try lhs_val.floatSub(rhs_val, scalar_type, sema.arena);
49644945 break :blk val;
49654946 },
49664947 .div => blk: {
49674948 const val = if (is_int)
4968 try Module.intDiv(sema.arena, lhs_val, rhs_val)
4949 try lhs_val.intDiv(rhs_val, sema.arena)
49694950 else
4970 try Module.floatDiv(sema.arena, scalar_type, src, lhs_val, rhs_val);
4951 try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena);
49714952 break :blk val;
49724953 },
49734954 .mul => blk: {
49744955 const val = if (is_int)
4975 try Module.intMul(sema.arena, lhs_val, rhs_val)
4956 try lhs_val.intMul(rhs_val, sema.arena)
49764957 else
4977 try Module.floatMul(sema.arena, scalar_type, src, lhs_val, rhs_val);
4958 try lhs_val.floatMul(rhs_val, scalar_type, sema.arena);
49784959 break :blk val;
49794960 },
49804961 else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}),
......@@ -6173,7 +6154,62 @@ fn zirPtrCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
61736154fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61746155 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
61756156 const src = inst_data.src();
6176 return sema.mod.fail(&block.base, src, "TODO: Sema.zirTruncate", .{});
6157 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
6158 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
6159 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
6160 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
6161 const operand = sema.resolveInst(extra.rhs);
6162 const operand_ty = sema.typeOf(operand);
6163 const mod = sema.mod;
6164 const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_ty);
6165 const src_is_comptime_int = try sema.requireIntegerType(block, operand_src, operand_ty);
6166
6167 if (dest_is_comptime_int) {
6168 return sema.coerce(block, dest_ty, operand, operand_src);
6169 }
6170
6171 const target = mod.getTarget();
6172 const src_info = operand_ty.intInfo(target);
6173 const dest_info = dest_ty.intInfo(target);
6174
6175 if (src_info.bits == 0 or dest_info.bits == 0) {
6176 return sema.addConstant(dest_ty, Value.initTag(.zero));
6177 }
6178
6179 if (!src_is_comptime_int) {
6180 if (src_info.signedness != dest_info.signedness) {
6181 return mod.fail(&block.base, operand_src, "expected {s} integer type, found '{}'", .{
6182 @tagName(dest_info.signedness), operand_ty,
6183 });
6184 }
6185 if (src_info.bits > 0 and src_info.bits < dest_info.bits) {
6186 const msg = msg: {
6187 const msg = try mod.errMsg(
6188 &block.base,
6189 src,
6190 "destination type '{}' has more bits than source type '{}'",
6191 .{ dest_ty, operand_ty },
6192 );
6193 errdefer msg.destroy(mod.gpa);
6194 try mod.errNote(&block.base, dest_ty_src, msg, "destination type has {d} bits", .{
6195 dest_info.bits,
6196 });
6197 try mod.errNote(&block.base, operand_src, msg, "source type has {d} bits", .{
6198 src_info.bits,
6199 });
6200 break :msg msg;
6201 };
6202 return mod.failWithOwnedErrorMsg(&block.base, msg);
6203 }
6204 }
6205
6206 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
6207 if (val.isUndef()) return sema.addConstUndef(dest_ty);
6208 return sema.addConstant(dest_ty, try val.intTrunc(sema.arena, dest_info.bits));
6209 }
6210
6211 try sema.requireRuntimeBlock(block, src);
6212 return block.addTyOp(.trunc, dest_ty, operand);
61776213}
61786214
61796215fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -6594,6 +6630,14 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void
65946630 try sema.requireFunctionBlock(block, src);
65956631}
65966632
6633fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !bool {
6634 switch (ty.zigTypeTag()) {
6635 .ComptimeInt => return true,
6636 .Int => return false,
6637 else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),
6638 }
6639}
6640
65976641fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
65986642 if (!ty.isValidVarType(false)) {
65996643 return sema.mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty});
src/codegen.zig+14
......@@ -835,6 +835,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
835835 .dbg_stmt => try self.airDbgStmt(inst),
836836 .floatcast => try self.airFloatCast(inst),
837837 .intcast => try self.airIntCast(inst),
838 .trunc => try self.airTrunc(inst),
838839 .bool_to_int => try self.airBoolToInt(inst),
839840 .is_non_null => try self.airIsNonNull(inst),
840841 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
......@@ -1109,6 +1110,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11091110 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11101111 }
11111112
1113 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
1114 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1115 if (self.liveness.isUnused(inst))
1116 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
1117
1118 const operand = try self.resolveInst(ty_op.operand);
1119 _ = operand;
1120 const result: MCValue = switch (arch) {
1121 else => return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}),
1122 };
1123 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1124 }
1125
11121126 fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
11131127 const un_op = self.air.instructions.items(.data)[inst].un_op;
11141128 const operand = try self.resolveInst(un_op);
src/codegen/c.zig+13-2
......@@ -900,6 +900,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
900900 .call => try airCall(o, inst),
901901 .dbg_stmt => try airDbgStmt(o, inst),
902902 .intcast => try airIntCast(o, inst),
903 .trunc => try airTrunc(o, inst),
903904 .bool_to_int => try airBoolToInt(o, inst),
904905 .load => try airLoad(o, inst),
905906 .ret => try airRet(o, inst),
......@@ -1038,7 +1039,7 @@ fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
10381039 return CValue.none;
10391040
10401041 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1041 const from = try o.resolveInst(ty_op.operand);
1042 const operand = try o.resolveInst(ty_op.operand);
10421043
10431044 const writer = o.writer();
10441045 const inst_ty = o.air.typeOfIndex(inst);
......@@ -1046,11 +1047,21 @@ fn airIntCast(o: *Object, inst: Air.Inst.Index) !CValue {
10461047 try writer.writeAll(" = (");
10471048 try o.dg.renderType(writer, inst_ty);
10481049 try writer.writeAll(")");
1049 try o.writeCValue(writer, from);
1050 try o.writeCValue(writer, operand);
10501051 try writer.writeAll(";\n");
10511052 return local;
10521053}
10531054
1055fn airTrunc(o: *Object, inst: Air.Inst.Index) !CValue {
1056 if (o.liveness.isUnused(inst))
1057 return CValue.none;
1058
1059 const ty_op = o.air.instructions.items(.data)[inst].ty_op;
1060 const operand = try o.resolveInst(ty_op.operand);
1061 _ = operand;
1062 return o.dg.fail("TODO: C backend: airTrunc", .{});
1063}
1064
10541065fn airBoolToInt(o: *Object, inst: Air.Inst.Index) !CValue {
10551066 if (o.liveness.isUnused(inst))
10561067 return CValue.none;
src/codegen/llvm.zig+11
......@@ -960,6 +960,7 @@ pub const FuncGen = struct {
960960 .call => try self.airCall(inst),
961961 .cond_br => try self.airCondBr(inst),
962962 .intcast => try self.airIntCast(inst),
963 .trunc => try self.airTrunc(inst),
963964 .floatcast => try self.airFloatCast(inst),
964965 .ptrtoint => try self.airPtrToInt(inst),
965966 .load => try self.airLoad(inst),
......@@ -1615,6 +1616,16 @@ pub const FuncGen = struct {
16151616 return self.builder.buildIntCast2(operand, try self.dg.llvmType(inst_ty), llvm.Bool.fromBool(signed), "");
16161617 }
16171618
1619 fn airTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1620 if (self.liveness.isUnused(inst))
1621 return null;
1622
1623 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1624 const operand = try self.resolveInst(ty_op.operand);
1625 const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
1626 return self.builder.buildTrunc(operand, dest_llvm_ty, "");
1627 }
1628
16181629 fn airFloatCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
16191630 if (self.liveness.isUnused(inst))
16201631 return null;
src/codegen/llvm/bindings.zig+8
......@@ -423,6 +423,14 @@ pub const Builder = opaque {
423423 Idx: c_uint,
424424 Name: [*:0]const u8,
425425 ) *const Value;
426
427 pub const buildTrunc = LLVMBuildTrunc;
428 extern fn LLVMBuildTrunc(
429 *const Builder,
430 Val: *const Value,
431 DestTy: *const Type,
432 Name: [*:0]const u8,
433 ) *const Value;
426434};
427435
428436pub const IntPredicate = enum(c_int) {
src/print_air.zig+1
......@@ -151,6 +151,7 @@ const Writer = struct {
151151 .load,
152152 .floatcast,
153153 .intcast,
154 .trunc,
154155 .optional_payload,
155156 .optional_payload_ptr,
156157 .wrap_optional,
src/value.zig+238
......@@ -1407,6 +1407,244 @@ pub const Value = extern union {
14071407 };
14081408 }
14091409
1410 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
1411 // TODO is this a performance issue? maybe we should try the operation without
1412 // resorting to BigInt first.
1413 var lhs_space: Value.BigIntSpace = undefined;
1414 var rhs_space: Value.BigIntSpace = undefined;
1415 const lhs_bigint = lhs.toBigInt(&lhs_space);
1416 const rhs_bigint = rhs.toBigInt(&rhs_space);
1417 const limbs = try allocator.alloc(
1418 std.math.big.Limb,
1419 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1420 );
1421 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1422 result_bigint.add(lhs_bigint, rhs_bigint);
1423 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1424
1425 if (result_bigint.positive) {
1426 return Value.Tag.int_big_positive.create(allocator, result_limbs);
1427 } else {
1428 return Value.Tag.int_big_negative.create(allocator, result_limbs);
1429 }
1430 }
1431
1432 pub fn intSub(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
1433 // TODO is this a performance issue? maybe we should try the operation without
1434 // resorting to BigInt first.
1435 var lhs_space: Value.BigIntSpace = undefined;
1436 var rhs_space: Value.BigIntSpace = undefined;
1437 const lhs_bigint = lhs.toBigInt(&lhs_space);
1438 const rhs_bigint = rhs.toBigInt(&rhs_space);
1439 const limbs = try allocator.alloc(
1440 std.math.big.Limb,
1441 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
1442 );
1443 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1444 result_bigint.sub(lhs_bigint, rhs_bigint);
1445 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1446
1447 if (result_bigint.positive) {
1448 return Value.Tag.int_big_positive.create(allocator, result_limbs);
1449 } else {
1450 return Value.Tag.int_big_negative.create(allocator, result_limbs);
1451 }
1452 }
1453
1454 pub fn intDiv(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
1455 // TODO is this a performance issue? maybe we should try the operation without
1456 // resorting to BigInt first.
1457 var lhs_space: Value.BigIntSpace = undefined;
1458 var rhs_space: Value.BigIntSpace = undefined;
1459 const lhs_bigint = lhs.toBigInt(&lhs_space);
1460 const rhs_bigint = rhs.toBigInt(&rhs_space);
1461 const limbs_q = try allocator.alloc(
1462 std.math.big.Limb,
1463 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
1464 );
1465 const limbs_r = try allocator.alloc(
1466 std.math.big.Limb,
1467 lhs_bigint.limbs.len,
1468 );
1469 const limbs_buffer = try allocator.alloc(
1470 std.math.big.Limb,
1471 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
1472 );
1473 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
1474 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
1475 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
1476 const result_limbs = result_q.limbs[0..result_q.len];
1477
1478 if (result_q.positive) {
1479 return Value.Tag.int_big_positive.create(allocator, result_limbs);
1480 } else {
1481 return Value.Tag.int_big_negative.create(allocator, result_limbs);
1482 }
1483 }
1484
1485 pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
1486 // TODO is this a performance issue? maybe we should try the operation without
1487 // resorting to BigInt first.
1488 var lhs_space: Value.BigIntSpace = undefined;
1489 var rhs_space: Value.BigIntSpace = undefined;
1490 const lhs_bigint = lhs.toBigInt(&lhs_space);
1491 const rhs_bigint = rhs.toBigInt(&rhs_space);
1492 const limbs = try allocator.alloc(
1493 std.math.big.Limb,
1494 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
1495 );
1496 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1497 var limbs_buffer = try allocator.alloc(
1498 std.math.big.Limb,
1499 std.math.big.int.calcMulLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len, 1),
1500 );
1501 defer allocator.free(limbs_buffer);
1502 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
1503 const result_limbs = result_bigint.limbs[0..result_bigint.len];
1504
1505 if (result_bigint.positive) {
1506 return Value.Tag.int_big_positive.create(allocator, result_limbs);
1507 } else {
1508 return Value.Tag.int_big_negative.create(allocator, result_limbs);
1509 }
1510 }
1511
1512 pub fn intTrunc(val: Value, arena: *Allocator, bits: u16) !Value {
1513 const x = val.toUnsignedInt(); // TODO: implement comptime truncate on big ints
1514 if (bits == 64) return val;
1515 const mask = (@as(u64, 1) << @intCast(u6, bits)) - 1;
1516 const truncated = x & mask;
1517 return Tag.int_u64.create(arena, truncated);
1518 }
1519
1520 pub fn floatAdd(
1521 lhs: Value,
1522 rhs: Value,
1523 float_type: Type,
1524 arena: *Allocator,
1525 ) !Value {
1526 switch (float_type.tag()) {
1527 .f16 => {
1528 @panic("TODO add __trunctfhf2 to compiler-rt");
1529 //const lhs_val = lhs.toFloat(f16);
1530 //const rhs_val = rhs.toFloat(f16);
1531 //return Value.Tag.float_16.create(arena, lhs_val + rhs_val);
1532 },
1533 .f32 => {
1534 const lhs_val = lhs.toFloat(f32);
1535 const rhs_val = rhs.toFloat(f32);
1536 return Value.Tag.float_32.create(arena, lhs_val + rhs_val);
1537 },
1538 .f64 => {
1539 const lhs_val = lhs.toFloat(f64);
1540 const rhs_val = rhs.toFloat(f64);
1541 return Value.Tag.float_64.create(arena, lhs_val + rhs_val);
1542 },
1543 .f128, .comptime_float, .c_longdouble => {
1544 const lhs_val = lhs.toFloat(f128);
1545 const rhs_val = rhs.toFloat(f128);
1546 return Value.Tag.float_128.create(arena, lhs_val + rhs_val);
1547 },
1548 else => unreachable,
1549 }
1550 }
1551
1552 pub fn floatSub(
1553 lhs: Value,
1554 rhs: Value,
1555 float_type: Type,
1556 arena: *Allocator,
1557 ) !Value {
1558 switch (float_type.tag()) {
1559 .f16 => {
1560 @panic("TODO add __trunctfhf2 to compiler-rt");
1561 //const lhs_val = lhs.toFloat(f16);
1562 //const rhs_val = rhs.toFloat(f16);
1563 //return Value.Tag.float_16.create(arena, lhs_val - rhs_val);
1564 },
1565 .f32 => {
1566 const lhs_val = lhs.toFloat(f32);
1567 const rhs_val = rhs.toFloat(f32);
1568 return Value.Tag.float_32.create(arena, lhs_val - rhs_val);
1569 },
1570 .f64 => {
1571 const lhs_val = lhs.toFloat(f64);
1572 const rhs_val = rhs.toFloat(f64);
1573 return Value.Tag.float_64.create(arena, lhs_val - rhs_val);
1574 },
1575 .f128, .comptime_float, .c_longdouble => {
1576 const lhs_val = lhs.toFloat(f128);
1577 const rhs_val = rhs.toFloat(f128);
1578 return Value.Tag.float_128.create(arena, lhs_val - rhs_val);
1579 },
1580 else => unreachable,
1581 }
1582 }
1583
1584 pub fn floatDiv(
1585 lhs: Value,
1586 rhs: Value,
1587 float_type: Type,
1588 arena: *Allocator,
1589 ) !Value {
1590 switch (float_type.tag()) {
1591 .f16 => {
1592 @panic("TODO add __trunctfhf2 to compiler-rt");
1593 //const lhs_val = lhs.toFloat(f16);
1594 //const rhs_val = rhs.toFloat(f16);
1595 //return Value.Tag.float_16.create(arena, lhs_val / rhs_val);
1596 },
1597 .f32 => {
1598 const lhs_val = lhs.toFloat(f32);
1599 const rhs_val = rhs.toFloat(f32);
1600 return Value.Tag.float_32.create(arena, lhs_val / rhs_val);
1601 },
1602 .f64 => {
1603 const lhs_val = lhs.toFloat(f64);
1604 const rhs_val = rhs.toFloat(f64);
1605 return Value.Tag.float_64.create(arena, lhs_val / rhs_val);
1606 },
1607 .f128, .comptime_float, .c_longdouble => {
1608 const lhs_val = lhs.toFloat(f128);
1609 const rhs_val = rhs.toFloat(f128);
1610 return Value.Tag.float_128.create(arena, lhs_val / rhs_val);
1611 },
1612 else => unreachable,
1613 }
1614 }
1615
1616 pub fn floatMul(
1617 lhs: Value,
1618 rhs: Value,
1619 float_type: Type,
1620 arena: *Allocator,
1621 ) !Value {
1622 switch (float_type.tag()) {
1623 .f16 => {
1624 @panic("TODO add __trunctfhf2 to compiler-rt");
1625 //const lhs_val = lhs.toFloat(f16);
1626 //const rhs_val = rhs.toFloat(f16);
1627 //return Value.Tag.float_16.create(arena, lhs_val * rhs_val);
1628 },
1629 .f32 => {
1630 const lhs_val = lhs.toFloat(f32);
1631 const rhs_val = rhs.toFloat(f32);
1632 return Value.Tag.float_32.create(arena, lhs_val * rhs_val);
1633 },
1634 .f64 => {
1635 const lhs_val = lhs.toFloat(f64);
1636 const rhs_val = rhs.toFloat(f64);
1637 return Value.Tag.float_64.create(arena, lhs_val * rhs_val);
1638 },
1639 .f128, .comptime_float, .c_longdouble => {
1640 const lhs_val = lhs.toFloat(f128);
1641 const rhs_val = rhs.toFloat(f128);
1642 return Value.Tag.float_128.create(arena, lhs_val * rhs_val);
1643 },
1644 else => unreachable,
1645 }
1646 }
1647
14101648 /// This type is not copyable since it may contain pointers to its inner data.
14111649 pub const Payload = struct {
14121650 tag: Tag,
test/behavior/basic.zig+11
......@@ -1,3 +1,6 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
14// normal comment
25
36/// this is a documentation comment
......@@ -7,3 +10,11 @@ fn emptyFunctionWithComments() void {}
710test "empty function with comments" {
811 emptyFunctionWithComments();
912}
13
14test "truncate" {
15 try expect(testTruncate(0x10fd) == 0xfd);
16 comptime try expect(testTruncate(0x10fd) == 0xfd);
17}
18fn testTruncate(x: u32) u8 {
19 return @truncate(u8, x);
20}
test/behavior/misc.zig-7
......@@ -5,13 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;
55const mem = std.mem;
66const builtin = @import("builtin");
77
8test "truncate" {
9 try expect(testTruncate(0x10fd) == 0xfd);
10}
11fn testTruncate(x: u32) u8 {
12 return @truncate(u8, x);
13}
14
158fn first4KeysOfHomeRow() []const u8 {
169 return "aoeu";
1710}