authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-22 13:28:57-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-22 13:28:57-05:00
log52bb71867d8d6e738eefb7dafead875eb21a4ae7
tree317c8b0f23e603c590f943700587c3a6596bc8ff
parent2fe8a0831f2830a3357cdf65b2ebfe1214b13a83
signature Commit is signed but in an unrecognized format.

implement vector negation

also fix vector behavior tests, they weren't actually testing runtime vectors, but now they are. See #903

5 files changed, 157 insertions(+), 47 deletions(-)

src/codegen.cpp+6-5
...@@ -3229,7 +3229,8 @@ static LLVMValueRef ir_render_br(CodeGen *g, IrExecutable *executable, IrInstruc...@@ -3229,7 +3229,8 @@ static LLVMValueRef ir_render_br(CodeGen *g, IrExecutable *executable, IrInstruc
3229static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInstructionUnOp *un_op_instruction) {3229static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInstructionUnOp *un_op_instruction) {
3230 IrUnOp op_id = un_op_instruction->op_id;3230 IrUnOp op_id = un_op_instruction->op_id;
3231 LLVMValueRef expr = ir_llvm_value(g, un_op_instruction->value);3231 LLVMValueRef expr = ir_llvm_value(g, un_op_instruction->value);
3232 ZigType *expr_type = un_op_instruction->value->value.type;3232 ZigType *operand_type = un_op_instruction->value->value.type;
3233 ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
32333234
3234 switch (op_id) {3235 switch (op_id) {
3235 case IrUnOpInvalid:3236 case IrUnOpInvalid:
...@@ -3239,16 +3240,16 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst...@@ -3239,16 +3240,16 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
3239 case IrUnOpNegation:3240 case IrUnOpNegation:
3240 case IrUnOpNegationWrap:3241 case IrUnOpNegationWrap:
3241 {3242 {
3242 if (expr_type->id == ZigTypeIdFloat) {3243 if (scalar_type->id == ZigTypeIdFloat) {
3243 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &un_op_instruction->base));3244 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &un_op_instruction->base));
3244 return LLVMBuildFNeg(g->builder, expr, "");3245 return LLVMBuildFNeg(g->builder, expr, "");
3245 } else if (expr_type->id == ZigTypeIdInt) {3246 } else if (scalar_type->id == ZigTypeIdInt) {
3246 if (op_id == IrUnOpNegationWrap) {3247 if (op_id == IrUnOpNegationWrap) {
3247 return LLVMBuildNeg(g->builder, expr, "");3248 return LLVMBuildNeg(g->builder, expr, "");
3248 } else if (ir_want_runtime_safety(g, &un_op_instruction->base)) {3249 } else if (ir_want_runtime_safety(g, &un_op_instruction->base)) {
3249 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));3250 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
3250 return gen_overflow_op(g, expr_type, AddSubMulSub, zero, expr);3251 return gen_overflow_op(g, operand_type, AddSubMulSub, zero, expr);
3251 } else if (expr_type->data.integral.is_signed) {3252 } else if (scalar_type->data.integral.is_signed) {
3252 return LLVMBuildNSWNeg(g->builder, expr, "");3253 return LLVMBuildNSWNeg(g->builder, expr, "");
3253 } else {3254 } else {
3254 return LLVMBuildNUWNeg(g->builder, expr, "");3255 return LLVMBuildNUWNeg(g->builder, expr, "");
src/ir.cpp+71-33
...@@ -14620,6 +14620,41 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_...@@ -14620,6 +14620,41 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_
14620 zig_unreachable();14620 zig_unreachable();
14621}14621}
1462214622
14623static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *scalar_type,
14624 ConstExprValue *operand_val, ConstExprValue *scalar_out_val, bool is_wrap_op)
14625{
14626 bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat);
14627
14628 bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) ||
14629 scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op));
14630
14631 if (!ok_type) {
14632 const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
14633 return ir_add_error(ira, source_instr, buf_sprintf(fmt, buf_ptr(&scalar_type->name)));
14634 }
14635
14636 if (is_float) {
14637 float_negate(scalar_out_val, operand_val);
14638 } else if (is_wrap_op) {
14639 bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint,
14640 scalar_type->data.integral.bit_count);
14641 } else {
14642 bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint);
14643 }
14644
14645 scalar_out_val->type = scalar_type;
14646 scalar_out_val->special = ConstValSpecialStatic;
14647
14648 if (is_wrap_op || is_float || scalar_type->id == ZigTypeIdComptimeInt) {
14649 return nullptr;
14650 }
14651
14652 if (!bigint_fits_in_bits(&scalar_out_val->data.x_bigint, scalar_type->data.integral.bit_count, true)) {
14653 return ir_add_error(ira, source_instr, buf_sprintf("negation caused overflow"));
14654 }
14655 return nullptr;
14656}
14657
14623static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *instruction) {14658static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *instruction) {
14624 IrInstruction *value = instruction->value->child;14659 IrInstruction *value = instruction->value->child;
14625 ZigType *expr_type = value->value.type;14660 ZigType *expr_type = value->value.type;
...@@ -14628,47 +14663,50 @@ static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *ins...@@ -14628,47 +14663,50 @@ static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *ins
1462814663
14629 bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);14664 bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);
1463014665
14631 bool is_float = (expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat);14666 ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
1463214667
14633 if ((expr_type->id == ZigTypeIdInt && expr_type->data.integral.is_signed) ||14668 if (instr_is_comptime(value)) {
14634 expr_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op))14669 ConstExprValue *operand_val = ir_resolve_const(ira, value, UndefBad);
14635 {14670 if (!operand_val)
14636 if (instr_is_comptime(value)) {14671 return ira->codegen->invalid_instruction;
14637 ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
14638 if (!target_const_val)
14639 return ira->codegen->invalid_instruction;
1464014672
14641 IrInstruction *result = ir_const(ira, &instruction->base, expr_type);14673 IrInstruction *result_instruction = ir_const(ira, &instruction->base, expr_type);
14642 ConstExprValue *out_val = &result->value;14674 ConstExprValue *out_val = &result_instruction->value;
14643 if (is_float) {14675 if (expr_type->id == ZigTypeIdVector) {
14644 float_negate(out_val, target_const_val);14676 expand_undef_array(ira->codegen, operand_val);
14645 } else if (is_wrap_op) {14677 out_val->special = ConstValSpecialUndef;
14646 bigint_negate_wrap(&out_val->data.x_bigint, &target_const_val->data.x_bigint,14678 expand_undef_array(ira->codegen, out_val);
14647 expr_type->data.integral.bit_count);14679 size_t len = expr_type->data.vector.len;
14648 } else {14680 for (size_t i = 0; i < len; i += 1) {
14649 bigint_negate(&out_val->data.x_bigint, &target_const_val->data.x_bigint);14681 ConstExprValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i];
14650 }14682 ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
14651 if (is_wrap_op || is_float || expr_type->id == ZigTypeIdComptimeInt) {14683 assert(scalar_operand_val->type == scalar_type);
14652 return result;14684 assert(scalar_out_val->type == scalar_type);
14685 ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base, scalar_type,
14686 scalar_operand_val, scalar_out_val, is_wrap_op);
14687 if (msg != nullptr) {
14688 add_error_note(ira->codegen, msg, instruction->base.source_node,
14689 buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
14690 return ira->codegen->invalid_instruction;
14691 }
14653 }14692 }
1465414693 out_val->type = expr_type;
14655 if (!bigint_fits_in_bits(&out_val->data.x_bigint, expr_type->data.integral.bit_count, true)) {14694 out_val->special = ConstValSpecialStatic;
14656 ir_add_error(ira, &instruction->base, buf_sprintf("negation caused overflow"));14695 } else {
14696 if (ir_eval_negation_scalar(ira, &instruction->base, scalar_type, operand_val, out_val,
14697 is_wrap_op) != nullptr)
14698 {
14657 return ira->codegen->invalid_instruction;14699 return ira->codegen->invalid_instruction;
14658 }14700 }
14659 return result;
14660 }14701 }
1466114702 return result_instruction;
14662 IrInstruction *result = ir_build_un_op(&ira->new_irb,
14663 instruction->base.scope, instruction->base.source_node,
14664 instruction->op_id, value);
14665 result->value.type = expr_type;
14666 return result;
14667 }14703 }
1466814704
14669 const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";14705 IrInstruction *result = ir_build_un_op(&ira->new_irb,
14670 ir_add_error(ira, &instruction->base, buf_sprintf(fmt, buf_ptr(&expr_type->name)));14706 instruction->base.scope, instruction->base.source_node,
14671 return ira->codegen->invalid_instruction;14707 instruction->op_id, value);
14708 result->value.type = expr_type;
14709 return result;
14672}14710}
1467314711
14674static IrInstruction *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) {14712static IrInstruction *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) {
test/compile_errors.zig+12
...@@ -1,6 +1,18 @@...@@ -1,6 +1,18 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.addTest(
5 "comptime vector overflow shows the index",
6 \\comptime {
7 \\ var a: @Vector(4, u8) = []u8{ 1, 2, 255, 4 };
8 \\ var b: @Vector(4, u8) = []u8{ 5, 6, 1, 8 };
9 \\ var x = a + b;
10 \\}
11 ,
12 ".tmp_source.zig:4:15: error: operation caused overflow",
13 ".tmp_source.zig:4:15: note: when computing vector element at index 2",
14 );
15
4 cases.addTest(16 cases.addTest(
5 "packed struct with fields of not allowed types",17 "packed struct with fields of not allowed types",
6 \\const A = packed struct {18 \\const A = packed struct {
test/runtime_safety.zig+41
...@@ -118,6 +118,47 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -118,6 +118,47 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
118 \\}118 \\}
119 );119 );
120120
121 cases.addRuntimeSafety("vector integer subtraction overflow",
122 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
123 \\ @import("std").os.exit(126);
124 \\}
125 \\pub fn main() void {
126 \\ var a: @Vector(4, u32) = []u32{ 1, 2, 8, 4 };
127 \\ var b: @Vector(4, u32) = []u32{ 5, 6, 7, 8 };
128 \\ const x = sub(b, a);
129 \\}
130 \\fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) {
131 \\ return a - b;
132 \\}
133 );
134
135 cases.addRuntimeSafety("vector integer multiplication overflow",
136 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
137 \\ @import("std").os.exit(126);
138 \\}
139 \\pub fn main() void {
140 \\ var a: @Vector(4, u8) = []u8{ 1, 2, 200, 4 };
141 \\ var b: @Vector(4, u8) = []u8{ 5, 6, 2, 8 };
142 \\ const x = mul(b, a);
143 \\}
144 \\fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) {
145 \\ return a * b;
146 \\}
147 );
148
149 cases.addRuntimeSafety("vector integer negation overflow",
150 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
151 \\ @import("std").os.exit(126);
152 \\}
153 \\pub fn main() void {
154 \\ var a: @Vector(4, i16) = []i16{ 1, -32768, 200, 4 };
155 \\ const x = neg(a);
156 \\}
157 \\fn neg(a: @Vector(4, i16)) @Vector(4, i16) {
158 \\ return -a;
159 \\}
160 );
161
121 cases.addRuntimeSafety("integer subtraction overflow",162 cases.addRuntimeSafety("integer subtraction overflow",
122 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {163 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
123 \\ @import("std").os.exit(126);164 \\ @import("std").os.exit(126);
test/stage1/behavior/vector.zig+27-9
...@@ -5,11 +5,28 @@ const expect = std.testing.expect;...@@ -5,11 +5,28 @@ const expect = std.testing.expect;
5test "vector wrap operators" {5test "vector wrap operators" {
6 const S = struct {6 const S = struct {
7 fn doTheTest() void {7 fn doTheTest() void {
8 const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };8 var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
9 const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };9 var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
10 expect(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 }));10 expect(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ -2147483648, 2147483645, 33, 44 }));
11 expect(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 }));11 expect(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 2147483646, 2147483647, 27, 36 }));
12 expect(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 }));12 expect(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 2147483647, 2, 90, 160 }));
13 var z: @Vector(4, i32) = [4]i32{ 1, 2, 3, -2147483648 };
14 expect(mem.eql(i32, ([4]i32)(-%z), [4]i32{ -1, -2, -3, -2147483648 }));
15 }
16 };
17 S.doTheTest();
18 comptime S.doTheTest();
19}
20
21test "vector int operators" {
22 const S = struct {
23 fn doTheTest() void {
24 var v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
25 var x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
26 expect(mem.eql(i32, ([4]i32)(v + x), [4]i32{ 11, 22, 33, 44 }));
27 expect(mem.eql(i32, ([4]i32)(v - x), [4]i32{ 9, 18, 27, 36 }));
28 expect(mem.eql(i32, ([4]i32)(v * x), [4]i32{ 10, 40, 90, 160 }));
29 expect(mem.eql(i32, ([4]i32)(-v), [4]i32{ -10, -20, -30, -40 }));
13 }30 }
14 };31 };
15 S.doTheTest();32 S.doTheTest();
...@@ -19,11 +36,12 @@ test "vector wrap operators" {...@@ -19,11 +36,12 @@ test "vector wrap operators" {
19test "vector float operators" {36test "vector float operators" {
20 const S = struct {37 const S = struct {
21 fn doTheTest() void {38 fn doTheTest() void {
22 const v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 };39 var v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 };
23 const x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 };40 var x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 };
24 expect(mem.eql(f32, ([4]f32)(v + x), [4]f32{ 11, 22, 33, 44 }));41 expect(mem.eql(f32, ([4]f32)(v + x), [4]f32{ 11, 22, 33, 44 }));
25 expect(mem.eql(f32, ([4]f32)(v - x), [4]f32{ 9, 18, 27, 36 }));42 expect(mem.eql(f32, ([4]f32)(v - x), [4]f32{ 9, 18, 27, 36 }));
26 expect(mem.eql(f32, ([4]f32)(v * x), [4]f32{ 10, 40, 90, 160 }));43 expect(mem.eql(f32, ([4]f32)(v * x), [4]f32{ 10, 40, 90, 160 }));
44 expect(mem.eql(f32, ([4]f32)(-x), [4]f32{ -1, -2, -3, -4 }));
27 }45 }
28 };46 };
29 S.doTheTest();47 S.doTheTest();
...@@ -33,8 +51,8 @@ test "vector float operators" {...@@ -33,8 +51,8 @@ test "vector float operators" {
33test "vector bit operators" {51test "vector bit operators" {
34 const S = struct {52 const S = struct {
35 fn doTheTest() void {53 fn doTheTest() void {
36 const v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 };54 var v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 };
37 const x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 };55 var x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 };
38 expect(mem.eql(u8, ([4]u8)(v ^ x), [4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 }));56 expect(mem.eql(u8, ([4]u8)(v ^ x), [4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 }));
39 expect(mem.eql(u8, ([4]u8)(v | x), [4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 }));57 expect(mem.eql(u8, ([4]u8)(v | x), [4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 }));
40 expect(mem.eql(u8, ([4]u8)(v & x), [4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 }));58 expect(mem.eql(u8, ([4]u8)(v & x), [4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 }));