From 912fc2890b1603ff8f6ffa09b15719322e852edf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 27 Jul 2019 11:33:22 +0100 Subject: Fix array comparisons and fix f32 literal LLVM issue regarding accurate representation --- src/ir_print.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'src/ir_print.cpp') diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b008fa682..a64f454d0 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -767,15 +767,17 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * case ExactValue_Float: { GB_ASSERT_MSG(is_type_float(type), "%s", type_to_string(type)); type = core_type(type); - u64 u = bit_cast(value.value_float); + u64 u_64 = bit_cast(value.value_float); + u32 u_32 = bit_cast(cast(f32)value.value_float); + #if 0 switch (type->Basic.kind) { case Basic_f32: // IMPORTANT NOTE(bill): LLVM requires all floating point constants to be // a 64 bit number if bits_of(float type) <= 64. // https://groups.google.com/forum/#!topic/llvm-dev/IlqV3TbSk6M - // 64 bit mantissa: 52 bits - // 32 bit mantissa: 23 bits - // 16 bit mantissa: 10 bits + // 64 bit mantissa: 52 bits ==> 52-52 == 0 + // 32 bit mantissa: 23 bits ==> 52-23 == 29 + // 16 bit mantissa: 10 bits ==> 52=10 == 42 // 29 == 52-23 u >>= 29; u <<= 29; @@ -792,6 +794,20 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_fprintf(f, "0x%016llx", u); break; } + #else + switch (type->Basic.kind) { + case Basic_f32: { + ir_fprintf(f, "bitcast (i32 %u to float)", u_32); + break; + } + case Basic_f64: + ir_fprintf(f, "0x%016llx", u_64); + break; + default: + ir_fprintf(f, "0x%016llx", u_64); + break; + } + #endif break; } case ExactValue_Complex: { -- cgit v1.2.3 From f1631812049ae2c50b9ddb2f5c163327f15731c8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 29 Jul 2019 10:43:07 +0100 Subject: Add extra hints for LLVM for implicit reference parameters --- src/ir_print.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/ir_print.cpp') diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a64f454d0..58e7b8b0d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1904,6 +1904,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { if (e->flags&EntityFlag_NoAlias) { ir_write_str_lit(f, " noalias"); } + if (e->flags&EntityFlag_ImplicitReference) { + ir_write_str_lit(f, " nonnull dereferenceable"); + } ir_write_byte(f, ' '); irValue *arg = call->args[i]; if (is_type_boolean(t)) { -- cgit v1.2.3 From 27a3c5449ad8b09e54c70a494170fed8157eb6c3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 9 Aug 2019 22:35:48 +0100 Subject: Fix global variable initialization for certain types. --- src/ir_print.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'src/ir_print.cpp') diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 58e7b8b0d..684ae81cd 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -2195,6 +2195,22 @@ void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) { ir_write_byte(f, '\n'); } +bool ir_print_global_type_allowed(Type *t) { + if (t == nullptr) { + return true; + } + t = core_type(t); + switch (t->kind) { + case Type_DynamicArray: + case Type_Map: + case Type_Union: + case Type_BitField: + return false; + } + + return true; +} + void print_llvm_ir(irGen *ir) { irModule *m = &ir->module; @@ -2358,7 +2374,7 @@ void print_llvm_ir(irGen *ir) { ir_print_type(f, m, g->entity->type); ir_write_byte(f, ' '); if (!g->is_foreign) { - if (g->value != nullptr) { + if (g->value != nullptr && ir_print_global_type_allowed(g->entity->type)) { ir_print_value(f, m, g->value, g->entity->type); } else { ir_write_string(f, str_lit("zeroinitializer")); -- cgit v1.2.3 From 0076a4df62c5f4e2fc827d6401ae4c8a93181fb9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 13 Aug 2019 22:33:05 +0100 Subject: Fix compound literal printing for structs with custom alignment requirements --- src/ir_print.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/ir_print.cpp') diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 684ae81cd..c1e41c4b0 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -943,7 +943,8 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * if (type->Struct.is_packed) ir_write_byte(f, '<'); ir_write_byte(f, '{'); if (type->Struct.custom_align > 0) { - ir_fprintf(f, "[0 x <%lld x i8>] zeroinitializer", cast(i64)type->Struct.custom_align); + ir_print_alignment_prefix_hack(f, cast(i64)type->Struct.custom_align); + ir_write_str_lit(f, " zeroinitializer"); if (value_count > 0) { ir_write_string(f, str_lit(", ")); } -- cgit v1.2.3 From 7bc146e6fde909298a7184fdf00cec91868ffc00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Aug 2019 11:33:05 +0100 Subject: Built-in Quaternions (Not just an April Fool's Joke any more) --- core/fmt/fmt.odin | 29 +++++ core/reflect/reflect.odin | 2 + core/runtime/core.odin | 23 ++-- core/runtime/internal.odin | 70 +++++++++++- examples/demo/demo.odin | 60 +++++++++-- src/check_expr.cpp | 242 +++++++++++++++++++++++++++++++++++++++-- src/checker.cpp | 6 ++ src/checker_builtin_procs.hpp | 6 ++ src/exact_value.cpp | 184 +++++++++++++++++++++++++++++-- src/ir.cpp | 245 ++++++++++++++++++++++++++++++++++++++++-- src/ir_print.cpp | 53 +++++++-- src/tokenizer.cpp | 5 +- src/types.cpp | 50 +++++++-- 13 files changed, 909 insertions(+), 66 deletions(-) (limited to 'src/ir_print.cpp') diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 5dbebf972..a2f1166ac 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1129,6 +1129,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Rune: fmt_arg(fi, v, verb); case runtime.Type_Info_Float: fmt_arg(fi, v, verb); case runtime.Type_Info_Complex: fmt_arg(fi, v, verb); + case runtime.Type_Info_Quaternion: fmt_arg(fi, v, verb); case runtime.Type_Info_String: fmt_arg(fi, v, verb); case runtime.Type_Info_Pointer: @@ -1386,6 +1387,31 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { } } +fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { + switch verb { + case 'f', 'F', 'v', 'h', 'H': + r, i, j, k := real(q), imag(q), jmag(q), kmag(q); + + fmt_float(fi, r, bits/4, verb); + + if !fi.plus && i >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, i, bits/4, verb); + strings.write_rune(fi.buf, 'i'); + + if !fi.plus && j >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, j, bits/4, verb); + strings.write_rune(fi.buf, 'j'); + + if !fi.plus && k >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, k, bits/4, verb); + strings.write_rune(fi.buf, 'k'); + + case: + fmt_bad_verb(fi, verb); + return; + } +} + fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { if arg == nil { strings.write_string(fi.buf, ""); @@ -1434,6 +1460,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case complex64: fmt_complex(fi, complex128(a), 64, verb); case complex128: fmt_complex(fi, a, 128, verb); + case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb); + case quaternion256: fmt_quaternion(fi, a, 256, verb); + case i8: fmt_int(fi, u64(a), true, 8, verb); case u8: fmt_int(fi, u64(a), false, 8, verb); case i16: fmt_int(fi, u64(a), true, 16, verb); diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index a4450490f..4bc9a2225 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -12,6 +12,7 @@ Type_Kind :: enum { Rune, Float, Complex, + Quaternion, String, Boolean, Any, @@ -42,6 +43,7 @@ type_kind :: proc(T: typeid) -> Type_Kind { case runtime.Type_Info_Rune: return .Rune; case runtime.Type_Info_Float: return .Float; case runtime.Type_Info_Complex: return .Complex; + case runtime.Type_Info_Quaternion: return .Quaternion; case runtime.Type_Info_String: return .String; case runtime.Type_Info_Boolean: return .Boolean; case runtime.Type_Info_Any: return .Any; diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 047281b10..022a84e9e 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -47,16 +47,17 @@ Type_Info_Endianness :: enum u8 { } // Variant Types -Type_Info_Named :: struct {name: string, base: ^Type_Info}; -Type_Info_Integer :: struct {signed: bool, endianness: Type_Info_Endianness}; -Type_Info_Rune :: struct {}; -Type_Info_Float :: struct {}; -Type_Info_Complex :: struct {}; -Type_Info_String :: struct {is_cstring: bool}; -Type_Info_Boolean :: struct {}; -Type_Info_Any :: struct {}; -Type_Info_Type_Id :: struct {}; -Type_Info_Pointer :: struct { +Type_Info_Named :: struct {name: string, base: ^Type_Info}; +Type_Info_Integer :: struct {signed: bool, endianness: Type_Info_Endianness}; +Type_Info_Rune :: struct {}; +Type_Info_Float :: struct {}; +Type_Info_Complex :: struct {}; +Type_Info_Quaternion :: struct {}; +Type_Info_String :: struct {is_cstring: bool}; +Type_Info_Boolean :: struct {}; +Type_Info_Any :: struct {}; +Type_Info_Type_Id :: struct {}; +Type_Info_Pointer :: struct { elem: ^Type_Info // nil -> rawptr }; Type_Info_Procedure :: struct { @@ -135,6 +136,7 @@ Type_Info :: struct { Type_Info_Rune, Type_Info_Float, Type_Info_Complex, + Type_Info_Quaternion, Type_Info_String, Type_Info_Boolean, Type_Info_Any, @@ -163,6 +165,7 @@ Typeid_Kind :: enum u8 { Rune, Float, Complex, + Quaternion, String, Boolean, Any, diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index c33d5f62c..a5099944b 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -357,6 +357,11 @@ complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return r complex128_ne :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); } +quaternion128_eq :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); } +quaternion128_ne :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); } + +quaternion256_eq :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); } +quaternion256_ne :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); } bounds_check_error :: proc "contextless" (file: string, line, column: int, index, count: int) { @@ -546,9 +551,16 @@ abs_complex128 :: inline proc "contextless" (x: complex128) -> f64 { r, i := real(x), imag(x); return _sqrt_f64(r*r + i*i); } +abs_quaternion128 :: inline proc "contextless" (x: quaternion128) -> f32 { + r, i, j, k := real(x), imag(x), jmag(x), kmag(x); + return _sqrt_f32(r*r + i*i + j*j + k*k); +} +abs_quaternion256 :: inline proc "contextless" (x: quaternion256) -> f64 { + r, i, j, k := real(x), imag(x), jmag(x), kmag(x); + return _sqrt_f64(r*r + i*i + j*j + k*k); +} - -quo_complex64 :: proc(n, m: complex64) -> complex64 { +quo_complex64 :: proc "contextless" (n, m: complex64) -> complex64 { e, f: f32; if abs(real(m)) >= abs(imag(m)) { @@ -566,7 +578,7 @@ quo_complex64 :: proc(n, m: complex64) -> complex64 { return complex(e, f); } -quo_complex128 :: proc(n, m: complex128) -> complex128 { +quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 { e, f: f64; if abs(real(m)) >= abs(imag(m)) { @@ -583,3 +595,55 @@ quo_complex128 :: proc(n, m: complex128) -> complex128 { return complex(e, f); } + +mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3; + t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2; + t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1; + t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0; + + return quaternion(t0, t1, t2, t3); +} + +mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3; + t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2; + t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1; + t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0; + + return quaternion(t0, t1, t2, t3); +} + +quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3); + + t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2; + t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2; + t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2; + t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2; + + return quaternion(t0, t1, t2, t3); +} + +quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3); + + t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2; + t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2; + t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2; + t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2; + + return quaternion(t0, t1, t2, t3); +} diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 343c50ff7..a96056b61 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -326,7 +326,7 @@ union_type :: proc() { } parametric_polymorphism :: proc() { - fmt.println("# parametric_polymorphism"); + fmt.println("\n# parametric_polymorphism"); print_value :: proc(value: $T) { fmt.printf("print_value: %T %v\n", value, value); @@ -561,7 +561,7 @@ prefix_table := [?]string{ threading_example :: proc() { when os.OS == "windows" { - fmt.println("# threading_example"); + fmt.println("\n# threading_example"); worker_proc :: proc(t: ^thread.Thread) -> int { for iteration in 1..5 { @@ -601,7 +601,7 @@ threading_example :: proc() { } array_programming :: proc() { - fmt.println("# array_programming"); + fmt.println("\n# array_programming"); { a := [3]f32{1, 2, 3}; b := [3]f32{5, 6, 7}; @@ -646,7 +646,7 @@ array_programming :: proc() { } named_proc_return_parameters :: proc() { - fmt.println("# named proc return parameters"); + fmt.println("\n# named proc return parameters"); foo0 :: proc() -> int { return 123; @@ -668,7 +668,7 @@ named_proc_return_parameters :: proc() { using_enum :: proc() { - fmt.println("# using enum"); + fmt.println("\n# using enum"); using Foo :: enum {A, B, C}; @@ -680,7 +680,7 @@ using_enum :: proc() { } map_type :: proc() { - fmt.println("# map type"); + fmt.println("\n# map type"); // enums of type u16, u32, i16 & i32 also work Enum_u8 :: enum u8 { @@ -735,7 +735,7 @@ map_type :: proc() { } implicit_selector_expression :: proc() { - fmt.println("# implicit selector expression"); + fmt.println("\n# implicit selector expression"); Foo :: enum {A, B, C}; @@ -763,7 +763,7 @@ implicit_selector_expression :: proc() { } explicit_procedure_overloading :: proc() { - fmt.println("# explicit procedure overloading"); + fmt.println("\n# explicit procedure overloading"); add_ints :: proc(a, b: int) -> int { x := a + b; @@ -797,7 +797,7 @@ explicit_procedure_overloading :: proc() { } complete_switch :: proc() { - fmt.println("# complete_switch"); + fmt.println("\n# complete_switch"); { // enum using Foo :: enum { A, @@ -947,7 +947,7 @@ deferred_procedure_associations :: proc() { } reflection :: proc() { - fmt.println("# reflection"); + fmt.println("\n# reflection"); Foo :: struct { x: int `tag1`, @@ -981,6 +981,45 @@ reflection :: proc() { } } +quaternions :: proc() { + fmt.println("\n# quaternions"); + + { // Quaternion operations + q := 1 + 2i + 3j + 4k; + r := quaternion(5, 6, 7, 8); + t := q * r; + fmt.printf("(%v) * (%v) = %v\n", q, r, t); + v := q / r; + fmt.printf("(%v) / (%v) = %v\n", q, r, v); + u := q + r; + fmt.printf("(%v) + (%v) = %v\n", q, r, u); + s := q - r; + fmt.printf("(%v) - (%v) = %v\n", q, r, s); + } + { // The quaternion types + q128: quaternion128; // 4xf32 + q256: quaternion256; // 4xf64 + q128 = quaternion(1, 0, 0, 0); + q256 = 1; // quaternion(1, 0, 0, 0); + } + { // Built-in procedures + q := 1 + 2i + 3j + 4k; + fmt.println("q =", q); + fmt.println("real(q) =", real(q)); + fmt.println("imag(q) =", imag(q)); + fmt.println("jmag(q) =", jmag(q)); + fmt.println("kmag(q) =", kmag(q)); + fmt.println("conj(q) =", conj(q)); + fmt.println("abs(q) =", abs(q)); + } + { // Conversion of a complex type to a quaternion type + c := 1 + 2i; + q := quaternion256(c); + fmt.println(c); + fmt.println(q); + } +} + main :: proc() { when true { general_stuff(); @@ -1000,5 +1039,6 @@ main :: proc() { diverging_procedures(); deferred_procedure_associations(); reflection(); + quaternions(); } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 24a130d62..6a9a53275 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -497,6 +497,14 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type if (is_type_complex(dst)) { return 1; } + if (is_type_quaternion(dst)) { + return 2; + } + break; + case Basic_UntypedQuaternion: + if (is_type_quaternion(dst)) { + return 1; + } break; } } @@ -1438,7 +1446,7 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ ExactValue imag = exact_value_imag(v); if (real.kind != ExactValue_Invalid && imag.kind != ExactValue_Invalid) { - if (out_value) *out_value = exact_binary_operator_value(Token_Add, real, exact_value_make_imag(imag)); + if (out_value) *out_value = exact_value_complex(exact_value_to_f64(real), exact_value_to_f64(imag)); return true; } break; @@ -1449,6 +1457,36 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ default: GB_PANIC("Compiler error: Unknown complex type!"); break; } + return false; + } else if (is_type_quaternion(type)) { + ExactValue v = exact_value_to_quaternion(in_value); + if (v.kind != ExactValue_Quaternion) { + return false; + } + + switch (type->Basic.kind) { + case Basic_quaternion128: + case Basic_quaternion256: { + ExactValue real = exact_value_real(v); + ExactValue imag = exact_value_imag(v); + ExactValue jmag = exact_value_jmag(v); + ExactValue kmag = exact_value_kmag(v); + if (real.kind != ExactValue_Invalid && + imag.kind != ExactValue_Invalid) { + if (out_value) *out_value = exact_value_quaternion(exact_value_to_f64(real), exact_value_to_f64(imag), exact_value_to_f64(jmag), exact_value_to_f64(kmag)); + return true; + } + break; + } + case Basic_UntypedComplex: + if (out_value) *out_value = exact_value_to_quaternion(*out_value); + return true; + case Basic_UntypedQuaternion: + return true; + + default: GB_PANIC("Compiler error: Unknown complex type!"); break; + } + return false; } else if (is_type_pointer(type)) { if (in_value.kind == ExactValue_Pointer) { @@ -1481,10 +1519,14 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { + #if 0 gbAllocator ha = heap_allocator(); String str = big_int_to_string(ha, &o->value.value_integer); defer (gb_free(ha, str.text)); error(o->expr, "'%s = %.*s' overflows '%s'", a, LIT(str), b); + #else + error(o->expr, "Cannot convert '%s' to '%s'", a, b); + #endif } } else { error(o->expr, "Cannot convert '%s' to '%s'", a, b); @@ -1759,6 +1801,21 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } break; } + } else if (is_type_quaternion(x->type) || is_type_quaternion(y->type)) { + switch (op) { + case Token_CmpEq: + switch (8*size) { + case 128: add_package_dependency(c, "runtime", "quaternion128_eq"); break; + case 256: add_package_dependency(c, "runtime", "quaternion256_eq"); break; + } + break; + case Token_NotEq: + switch (8*size) { + case 128: add_package_dependency(c, "runtime", "quaternion128_ne"); break; + case 256: add_package_dependency(c, "runtime", "quaternion256_ne"); break; + } + break; + } } } @@ -1978,6 +2035,14 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + if (is_type_complex(src) && is_type_quaternion(dst)) { + return true; + } + + if (is_type_quaternion(src) && is_type_quaternion(dst)) { + return true; + } + if (is_type_bit_field_value(src) && is_type_integer(dst)) { return true; } @@ -2557,6 +2622,8 @@ ExactValue convert_exact_value_for_type(ExactValue v, Type *type) { v = exact_value_to_integer(v); } else if (is_type_complex(t)) { v = exact_value_to_complex(v); + } else if (is_type_quaternion(t)) { + v = exact_value_to_quaternion(v); } return v; } @@ -2652,6 +2719,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { case Basic_UntypedInteger: case Basic_UntypedFloat: case Basic_UntypedComplex: + case Basic_UntypedQuaternion: case Basic_UntypedRune: if (!is_type_numeric(target_type)) { operand->mode = Addressing_Invalid; @@ -3769,10 +3837,98 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } + case BuiltinProc_quaternion: { + // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type + Operand x = *operand; + Operand y = {}; + Operand z = {}; + Operand w = {}; + + // NOTE(bill): Invalid will be the default till fixed + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + + check_expr(c, &y, ce->args[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &z, ce->args[2]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &w, ce->args[3]); + if (y.mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false; + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant && + z.mode == Addressing_Constant && + w.mode == Addressing_Constant) { + if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.type = t_untyped_float; + } + if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + y.type = t_untyped_float; + } + if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) { + z.type = t_untyped_float; + } + if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) { + w.type = t_untyped_float; + } + } + + if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) { + gbString tx = type_to_string(x.type); + gbString ty = type_to_string(y.type); + gbString tz = type_to_string(z.type); + gbString tw = type_to_string(w.type); + error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw); + gb_string_free(tw); + gb_string_free(tz); + gb_string_free(ty); + gb_string_free(tx); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments have type '%s', expected a floating point", s); + gb_string_free(s); + return false; + } + + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + f64 j = exact_value_to_float(z.value).value_float; + f64 k = exact_value_to_float(w.value).value_float; + operand->value = exact_value_quaternion(r, i, j, k); + operand->mode = Addressing_Constant; + } else { + operand->mode = Addressing_Value; + } + + BasicKind kind = core_type(x.type)->Basic.kind; + switch (kind) { + case Basic_f32: operand->type = t_quaternion128; break; + case Basic_f64: operand->type = t_quaternion256; break; + case Basic_UntypedFloat: operand->type = t_untyped_quaternion; break; + default: GB_PANIC("Invalid type"); break; + } + + break; + } + case BuiltinProc_real: case BuiltinProc_imag: { // real :: proc(x: type) -> float_type - // proc imag(x: type) -> float_type + // imag :: proc(x: type) -> float_type Operand *x = operand; if (is_type_untyped(x->type)) { @@ -3780,7 +3936,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (is_type_numeric(x->type)) { x->type = t_untyped_complex; } - } else { + } else if (is_type_quaternion(x->type)) { + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } else{ convert_to_typed(c, x, t_complex128); if (x->mode == Addressing_Invalid) { return false; @@ -3788,9 +3949,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } - if (!is_type_complex(x->type)) { + if (!is_type_complex(x->type) && !is_type_quaternion(x->type)) { gbString s = type_to_string(x->type); - error(call, "Argument has type '%s', expected a complex type", s); + error(call, "Argument has type '%s', expected a complex or quaternion type", s); gb_string_free(s); return false; } @@ -3808,7 +3969,57 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 switch (kind) { case Basic_complex64: x->type = t_f32; break; case Basic_complex128: x->type = t_f64; break; + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + + break; + } + + case BuiltinProc_jmag: + case BuiltinProc_kmag: { + // jmag :: proc(x: type) -> float_type + // kmag :: proc(x: type) -> float_type + + Operand *x = operand; + if (is_type_untyped(x->type)) { + if (x->mode == Addressing_Constant) { + if (is_type_numeric(x->type)) { + x->type = t_untyped_complex; + } + } else{ + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_quaternion(x->type)) { + gbString s = type_to_string(x->type); + error(call, "Argument has type '%s', expected a quaternion type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + switch (id) { + case BuiltinProc_jmag: x->value = exact_value_jmag(x->value); break; + case BuiltinProc_kmag: x->value = exact_value_kmag(x->value); break; + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; default: GB_PANIC("Invalid type"); break; } @@ -3822,12 +4033,24 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x->mode == Addressing_Constant) { ExactValue v = exact_value_to_complex(x->value); f64 r = v.value_complex.real; - f64 i = v.value_complex.imag; + f64 i = -v.value_complex.imag; x->value = exact_value_complex(r, i); x->mode = Addressing_Constant; } else { x->mode = Addressing_Value; } + } else if (is_type_quaternion(x->type)) { + if (x->mode == Addressing_Constant) { + ExactValue v = exact_value_to_quaternion(x->value); + f64 r = v.value_quaternion.real; + f64 i = -v.value_quaternion.imag; + f64 j = -v.value_quaternion.jmag; + f64 k = -v.value_quaternion.kmag; + x->value = exact_value_quaternion(r, i, j, k); + x->mode = Addressing_Constant; + } else { + x->mode = Addressing_Value; + } } else { gbString s = type_to_string(x->type); error(call, "Expected a complex or quaternion, got '%s'", s); @@ -4226,6 +4449,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "abs_f64"); if (are_types_identical(bt, t_complex64)) add_package_dependency(c, "runtime", "abs_complex64"); if (are_types_identical(bt, t_complex128)) add_package_dependency(c, "runtime", "abs_complex128"); + if (are_types_identical(bt, t_quaternion128)) add_package_dependency(c, "runtime", "abs_quaternion128"); + if (are_types_identical(bt, t_quaternion256)) add_package_dependency(c, "runtime", "abs_quaternion256"); } } @@ -6232,8 +6457,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case Token_Imag: { String s = bl->token.string; Rune r = s[s.len-1]; + // NOTE(bill, 2019-08-25): Allow for quaternions by having j and k imaginary numbers switch (r) { - case 'i': t = t_untyped_complex; break; + case 'i': t = t_untyped_complex; break; + case 'j': t = t_untyped_quaternion; break; + case 'k': t = t_untyped_quaternion; break; } break; diff --git a/src/checker.cpp b/src/checker.cpp index 45b661a58..d6f5ad402 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1610,6 +1610,10 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("quo_complex64"), str_lit("quo_complex128"), + str_lit("mul_quaternion128"), + str_lit("mul_quaternion256"), + str_lit("quo_quaternion128"), + str_lit("quo_quaternion256"), str_lit("umodti3"), str_lit("udivti3"), @@ -1891,6 +1895,7 @@ void init_core_type_info(Checker *c) { t_type_info_integer = find_core_type(c, str_lit("Type_Info_Integer")); t_type_info_rune = find_core_type(c, str_lit("Type_Info_Rune")); t_type_info_float = find_core_type(c, str_lit("Type_Info_Float")); + t_type_info_quaternion = find_core_type(c, str_lit("Type_Info_Quaternion")); t_type_info_complex = find_core_type(c, str_lit("Type_Info_Complex")); t_type_info_string = find_core_type(c, str_lit("Type_Info_String")); t_type_info_boolean = find_core_type(c, str_lit("Type_Info_Boolean")); @@ -1915,6 +1920,7 @@ void init_core_type_info(Checker *c) { t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); t_type_info_rune_ptr = alloc_type_pointer(t_type_info_rune); t_type_info_float_ptr = alloc_type_pointer(t_type_info_float); + t_type_info_quaternion_ptr = alloc_type_pointer(t_type_info_quaternion); t_type_info_complex_ptr = alloc_type_pointer(t_type_info_complex); t_type_info_string_ptr = alloc_type_pointer(t_type_info_string); t_type_info_boolean_ptr = alloc_type_pointer(t_type_info_boolean); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 536d47616..0135b3c50 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -16,8 +16,11 @@ enum BuiltinProcId { BuiltinProc_swizzle, BuiltinProc_complex, + BuiltinProc_quaternion, BuiltinProc_real, BuiltinProc_imag, + BuiltinProc_jmag, + BuiltinProc_kmag, BuiltinProc_conj, BuiltinProc_expand_to_tuple, @@ -172,8 +175,11 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("quaternion"), 4, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("jmag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("kmag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 520812e44..54d4ef6f2 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -12,6 +12,19 @@ bool are_types_identical(Type *x, Type *y); struct Complex128 { f64 real, imag; }; +struct Quaternion256 { + f64 imag, jmag, kmag, real; +}; + +Quaternion256 quaternion256_inverse(Quaternion256 x) { + f64 invmag2 = 1.0 / (x.real*x.real + x.imag*x.imag + x.jmag*x.jmag + x.kmag*x.kmag); + x.real = +x.real * invmag2; + x.imag = -x.imag * invmag2; + x.jmag = -x.jmag * invmag2; + x.kmag = -x.kmag * invmag2; + return x; +} + enum ExactValueKind { ExactValue_Invalid, @@ -21,6 +34,7 @@ enum ExactValueKind { ExactValue_Integer, ExactValue_Float, ExactValue_Complex, + ExactValue_Quaternion, ExactValue_Pointer, ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Procedure, // TODO(bill): Is this good enough? @@ -37,6 +51,7 @@ struct ExactValue { f64 value_float; i64 value_pointer; Complex128 value_complex; + Quaternion256 value_quaternion; Ast * value_compound; Ast * value_procedure; }; @@ -61,7 +76,8 @@ HashKey hash_exact_value(ExactValue v) { return hash_integer(v.value_pointer); case ExactValue_Complex: return hashing_proc(&v.value_complex, gb_size_of(Complex128)); - + case ExactValue_Quaternion: + return hashing_proc(&v.value_quaternion, gb_size_of(Quaternion256)); case ExactValue_Compound: return hash_pointer(v.value_compound); case ExactValue_Procedure: @@ -116,6 +132,15 @@ ExactValue exact_value_complex(f64 real, f64 imag) { return result; } +ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) { + ExactValue result = {ExactValue_Quaternion}; + result.value_quaternion.real = real; + result.value_quaternion.imag = imag; + result.value_quaternion.jmag = jmag; + result.value_quaternion.kmag = kmag; + return result; +} + ExactValue exact_value_pointer(i64 ptr) { ExactValue result = {ExactValue_Pointer}; result.value_pointer = ptr; @@ -253,8 +278,10 @@ ExactValue exact_value_from_basic_literal(Token token) { str.len--; // Ignore the 'i|j|k' f64 imag = float_from_string(str); - if (last_rune == 'i') { - return exact_value_complex(0, imag); + switch (last_rune) { + case 'i': return exact_value_complex(0, imag); + case 'j': return exact_value_quaternion(0, 0, imag, 0); + case 'k': return exact_value_quaternion(0, 0, 0, imag); } } case Token_Rune: { @@ -318,11 +345,26 @@ ExactValue exact_value_to_complex(ExactValue v) { return exact_value_complex(v.value_float, 0); case ExactValue_Complex: return v; + // case ExactValue_Quaternion: + // return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} +ExactValue exact_value_to_quaternion(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(big_int_to_f64(&v.value_integer), 0, 0, 0); + case ExactValue_Float: + return exact_value_quaternion(v.value_float, 0, 0, 0); + case ExactValue_Complex: + return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0); + case ExactValue_Quaternion: + return v; } ExactValue r = {ExactValue_Invalid}; return r; } - ExactValue exact_value_real(ExactValue v) { switch (v.kind) { @@ -331,6 +373,8 @@ ExactValue exact_value_real(ExactValue v) { return v; case ExactValue_Complex: return exact_value_float(v.value_complex.real); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.real); } ExactValue r = {ExactValue_Invalid}; return r; @@ -343,6 +387,34 @@ ExactValue exact_value_imag(ExactValue v) { return exact_value_i64(0); case ExactValue_Complex: return exact_value_float(v.value_complex.imag); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_jmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + case ExactValue_Complex: + return exact_value_i64(0); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.jmag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_kmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + case ExactValue_Complex: + return exact_value_i64(0); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.kmag); } ExactValue r = {ExactValue_Invalid}; return r; @@ -361,6 +433,32 @@ ExactValue exact_value_make_imag(ExactValue v) { return r; } +ExactValue exact_value_make_jmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0); + case ExactValue_Float: + return exact_value_quaternion(0, 0, v.value_float, 0); + default: + GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_make_kmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float); + case ExactValue_Float: + return exact_value_quaternion(0, 0, 0, v.value_float); + default: + GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + i64 exact_value_to_i64(ExactValue v) { v = exact_value_to_integer(v); if (v.kind == ExactValue_Integer) { @@ -389,6 +487,7 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, case ExactValue_Integer: case ExactValue_Float: case ExactValue_Complex: + case ExactValue_Quaternion: return v; } break; @@ -413,6 +512,13 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, f64 imag = v.value_complex.imag; return exact_value_complex(-real, -imag); } + case ExactValue_Quaternion: { + f64 real = v.value_quaternion.real; + f64 imag = v.value_quaternion.imag; + f64 jmag = v.value_quaternion.jmag; + f64 kmag = v.value_quaternion.kmag; + return exact_value_quaternion(-real, -imag, -jmag, -kmag); + } } break; } @@ -463,8 +569,10 @@ i32 exact_value_order(ExactValue const &v) { return 3; case ExactValue_Complex: return 4; - case ExactValue_Pointer: + case ExactValue_Quaternion: return 5; + case ExactValue_Pointer: + return 6; default: GB_PANIC("How'd you get here? Invalid Value.kind"); @@ -485,7 +593,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: - case ExactValue_Complex: + case ExactValue_Quaternion: return; case ExactValue_Integer: @@ -499,6 +607,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Complex: *x = exact_value_complex(big_int_to_f64(&x->value_integer), 0); return; + case ExactValue_Quaternion: + *x = exact_value_quaternion(big_int_to_f64(&x->value_integer), 0, 0, 0); + return; } break; @@ -509,6 +620,17 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Complex: *x = exact_value_to_complex(*x); return; + case ExactValue_Quaternion: + *x = exact_value_to_quaternion(*x); + return; + } + break; + + case ExactValue_Complex: + switch (y->kind) { + case ExactValue_Quaternion: + *x = exact_value_to_quaternion(*x); + return; } break; } @@ -606,6 +728,56 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) break; } + case ExactValue_Quaternion: { + y = exact_value_to_quaternion(y); + f64 xr = x.value_quaternion.real; + f64 xi = x.value_quaternion.imag; + f64 xj = x.value_quaternion.jmag; + f64 xk = x.value_quaternion.kmag; + f64 yr = y.value_quaternion.real; + f64 yi = y.value_quaternion.imag; + f64 yj = y.value_quaternion.jmag; + f64 yk = y.value_quaternion.kmag; + + + f64 real = 0; + f64 imag = 0; + f64 jmag = 0; + f64 kmag = 0; + + switch (op) { + case Token_Add: + real = xr + yr; + imag = xi + yi; + jmag = xj + yj; + kmag = xk + yk; + break; + case Token_Sub: + real = xr - yr; + imag = xi - yi; + jmag = xj - yj; + kmag = xk - yk; + break; + case Token_Mul: + imag = xr * yi + xi * yr + xj * yk - xk * yj; + jmag = xr * yj - xi * yk + xj * yr + xk * yi; + kmag = xr * yk + xi * yj - xj * yi + xk * yr; + real = xr * yr - xi * yi - xj * yj - xk * yk; + break; + case Token_Quo: { + f64 invmag2 = 1.0 / (yr*yr + yi*yi + yj*yj + yk*yk); + imag = (xr * -yi + xi * +yr + xj * -yk - xk * -yj) * invmag2; + jmag = (xr * -yj - xi * -yk + xj * +yr + xk * -yi) * invmag2; + kmag = (xr * -yk + xi * -yj - xj * -yi + xk * +yr) * invmag2; + real = (xr * +yr - xi * -yi - xj * -yj - xk * -yk) * invmag2; + break; + } + default: goto error; + } + return exact_value_quaternion(real, imag, jmag, kmag); + break; + } + case ExactValue_String: { if (op != Token_Add) goto error; diff --git a/src/ir.cpp b/src/ir.cpp index 2499b848f..2a4bb028b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3853,6 +3853,60 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * return ir_emit_load(proc, res); } + if (is_type_quaternion(t_left)) { + ir_emit_comment(proc, str_lit("complex.arith.begin")); + defer (ir_emit_comment(proc, str_lit("complex.arith.end"))); + + right = ir_emit_conv(proc, right, t_left); + + Type *ft = base_complex_elem_type(t_left); + + if (op == Token_Add || op == Token_Sub) { + irValue *res = ir_add_local_generated(proc, type, false); // NOTE: initialized in full later + irValue *x0 = ir_emit_struct_ev(proc, left, 0); + irValue *x1 = ir_emit_struct_ev(proc, left, 1); + irValue *x2 = ir_emit_struct_ev(proc, left, 2); + irValue *x3 = ir_emit_struct_ev(proc, left, 3); + + irValue *y0 = ir_emit_struct_ev(proc, right, 0); + irValue *y1 = ir_emit_struct_ev(proc, right, 1); + irValue *y2 = ir_emit_struct_ev(proc, right, 2); + irValue *y3 = ir_emit_struct_ev(proc, right, 3); + + irValue *z0 = ir_emit_arith(proc, op, x0, y0, ft); + irValue *z1 = ir_emit_arith(proc, op, x1, y1, ft); + irValue *z2 = ir_emit_arith(proc, op, x2, y2, ft); + irValue *z3 = ir_emit_arith(proc, op, x3, y3, ft); + + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), z0); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), z1); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), z2); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), z3); + + return ir_emit_load(proc, res); + } else if (op == Token_Mul) { + auto args = array_make(heap_allocator(), 2); + args[0] = left; + args[1] = right; + + switch (8*type_size_of(ft)) { + case 32: return ir_emit_runtime_call(proc, "mul_quaternion128", args); + case 64: return ir_emit_runtime_call(proc, "mul_quaternion256", args); + default: GB_PANIC("Unknown float type"); break; + } + } else if (op == Token_Quo) { + auto args = array_make(heap_allocator(), 2); + args[0] = left; + args[1] = right; + + switch (8*type_size_of(ft)) { + case 32: return ir_emit_runtime_call(proc, "quo_quaternion128", args); + case 64: return ir_emit_runtime_call(proc, "quo_quaternion256", args); + default: GB_PANIC("Unknown float type"); break; + } + } + } + #if 0 if (op == Token_Add) { @@ -4188,7 +4242,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal } if (is_type_complex(a)) { - char *runtime_proc = ""; + char const *runtime_proc = ""; i64 sz = 8*type_size_of(a); switch (sz) { case 64: @@ -4212,6 +4266,31 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal return ir_emit_runtime_call(proc, runtime_proc, args); } + if (is_type_quaternion(a)) { + char const *runtime_proc = ""; + i64 sz = 8*type_size_of(a); + switch (sz) { + case 128: + switch (op_kind) { + case Token_CmpEq: runtime_proc = "quaternion128_eq"; break; + case Token_NotEq: runtime_proc = "quaternion128_ne"; break; + } + break; + case 256: + switch (op_kind) { + case Token_CmpEq: runtime_proc = "quaternion256_eq"; break; + case Token_NotEq: runtime_proc = "quaternion256_ne"; break; + } + break; + } + GB_ASSERT(runtime_proc != nullptr); + + auto args = array_make(ir_allocator(), 2); + args[0] = left; + args[1] = right; + return ir_emit_runtime_call(proc, runtime_proc, args); + } + if (is_type_bit_set(a)) { switch (op_kind) { case Token_Lt: @@ -4295,11 +4374,18 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { case 0: result_type = alloc_type_pointer(ft); break; case 1: result_type = alloc_type_pointer(ft); break; } + } else if (is_type_quaternion(t)) { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = alloc_type_pointer(ft); break; + case 1: result_type = alloc_type_pointer(ft); break; + case 2: result_type = alloc_type_pointer(ft); break; + case 3: result_type = alloc_type_pointer(ft); break; + } } else if (is_type_slice(t)) { switch (index) { case 0: result_type = alloc_type_pointer(alloc_type_pointer(t->Slice.elem)); break; case 1: result_type = alloc_type_pointer(t_int); break; - case 2: result_type = alloc_type_pointer(t_int); break; } } else if (is_type_string(t)) { switch (index) { @@ -4370,6 +4456,17 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { } break; } + case Basic_quaternion128: case Basic_quaternion256: + { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = ft; break; + case 1: result_type = ft; break; + case 2: result_type = ft; break; + case 3: result_type = ft; break; + } + break; + } } break; case Type_Struct: @@ -4752,6 +4849,8 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { ev = exact_value_to_float(ev); } else if (is_type_complex(dst)) { ev = exact_value_to_complex(ev); + } else if (is_type_quaternion(dst)) { + ev = exact_value_to_quaternion(ev); } else if (is_type_string(dst)) { // Handled elsewhere GB_ASSERT_MSG(ev.kind == ExactValue_String, "%d", ev.kind); @@ -4875,6 +4974,49 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { return ir_emit_load(proc, gen); } + if (is_type_quaternion(src) && is_type_quaternion(dst)) { + // @QuaternionLayout + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, false); + irValue *q0 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft); + irValue *q1 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft); + irValue *q2 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 2), ft); + irValue *q3 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 3), ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), q0); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 1), q1); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 2), q2); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), q3); + return ir_emit_load(proc, gen); + } + + if (is_type_float(src) && is_type_complex(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, value, ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), real); + return ir_emit_load(proc, gen); + } + if (is_type_float(src) && is_type_quaternion(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, value, ft); + // @QuaternionLayout + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), real); + return ir_emit_load(proc, gen); + } + if (is_type_complex(src) && is_type_quaternion(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft); + irValue *imag = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft); + // @QuaternionLayout + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), imag); + return ir_emit_load(proc, gen); + } + + + // float <-> integer if (is_type_float(src) && is_type_integer(dst)) { irConvKind kind = irConv_fptosi; @@ -5315,6 +5457,7 @@ enum Typeid_Kind : u8 { Typeid_Rune, Typeid_Float, Typeid_Complex, + Typeid_Quaternion, Typeid_String, Typeid_Boolean, Typeid_Any, @@ -6067,17 +6210,77 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return ir_emit_load(proc, dst); } + case BuiltinProc_quaternion: { + ir_emit_comment(proc, str_lit("quaternion")); + irValue *real = ir_build_expr(proc, ce->args[0]); + irValue *imag = ir_build_expr(proc, ce->args[1]); + irValue *jmag = ir_build_expr(proc, ce->args[2]); + irValue *kmag = ir_build_expr(proc, ce->args[3]); + + // @QuaternionLayout + irValue *dst = ir_add_local_generated(proc, tv.type, false); + Type *ft = base_complex_elem_type(tv.type); + real = ir_emit_conv(proc, real, ft); + imag = ir_emit_conv(proc, imag, ft); + jmag = ir_emit_conv(proc, jmag, ft); + kmag = ir_emit_conv(proc, kmag, ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 0), imag); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 1), jmag); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 2), kmag); + + return ir_emit_load(proc, dst); + } + case BuiltinProc_real: { ir_emit_comment(proc, str_lit("real")); irValue *val = ir_build_expr(proc, ce->args[0]); - irValue *real = ir_emit_struct_ev(proc, val, 0); - return ir_emit_conv(proc, real, tv.type); + if (is_type_complex(ir_type(val))) { + irValue *real = ir_emit_struct_ev(proc, val, 0); + return ir_emit_conv(proc, real, tv.type); + } else if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *real = ir_emit_struct_ev(proc, val, 3); + return ir_emit_conv(proc, real, tv.type); + } + GB_PANIC("invalid type for real"); + return nullptr; } case BuiltinProc_imag: { ir_emit_comment(proc, str_lit("imag")); irValue *val = ir_build_expr(proc, ce->args[0]); - irValue *imag = ir_emit_struct_ev(proc, val, 1); - return ir_emit_conv(proc, imag, tv.type); + if (is_type_complex(ir_type(val))) { + irValue *imag = ir_emit_struct_ev(proc, val, 1); + return ir_emit_conv(proc, imag, tv.type); + } else if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 0); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for imag"); + return nullptr; + } + case BuiltinProc_jmag: { + ir_emit_comment(proc, str_lit("jmag")); + irValue *val = ir_build_expr(proc, ce->args[0]); + if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 1); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for jmag"); + return nullptr; + } + case BuiltinProc_kmag: { + ir_emit_comment(proc, str_lit("kmag")); + irValue *val = ir_build_expr(proc, ce->args[0]); + if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 2); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for kmag"); + return nullptr; } case BuiltinProc_conj: { @@ -6092,6 +6295,20 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag)); ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real); ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag); + } else if (is_type_quaternion(t)) { + // @QuaternionLayout + res = ir_add_local_generated(proc, tv.type, false); + irValue *real = ir_emit_struct_ev(proc, val, 3); + irValue *imag = ir_emit_struct_ev(proc, val, 0); + irValue *jmag = ir_emit_struct_ev(proc, val, 1); + irValue *kmag = ir_emit_struct_ev(proc, val, 2); + imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag)); + jmag = ir_emit_unary_arith(proc, Token_Sub, jmag, ir_type(jmag)); + kmag = ir_emit_unary_arith(proc, Token_Sub, kmag, ir_type(kmag)); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), imag); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), jmag); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), kmag); } return ir_emit_load(proc, res); } @@ -6162,7 +6379,16 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return x; } ir_emit_comment(proc, str_lit("abs")); - if (is_type_complex(t)) { + if (is_type_quaternion(t)) { + i64 sz = 8*type_size_of(t); + auto args = array_make(ir_allocator(), 1); + args[0] = x; + switch (sz) { + case 128: return ir_emit_runtime_call(proc, "abs_quaternion128", args); + case 256: return ir_emit_runtime_call(proc, "abs_quaternion256", args); + } + GB_PANIC("Unknown complex type"); + } else if (is_type_complex(t)) { i64 sz = 8*type_size_of(t); auto args = array_make(ir_allocator(), 1); args[0] = x; @@ -9876,6 +10102,11 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info tag = ir_emit_conv(proc, variant_ptr, t_type_info_complex_ptr); break; + case Basic_quaternion128: + case Basic_quaternion256: + tag = ir_emit_conv(proc, variant_ptr, t_type_info_quaternion_ptr); + break; + case Basic_rawptr: tag = ir_emit_conv(proc, variant_ptr, t_type_info_pointer_ptr); break; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c1e41c4b0..30ad914a9 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -418,20 +418,23 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { } return; - // case Basic_f16: ir_write_str_lit(f, "half"); return; - case Basic_f32: ir_write_str_lit(f, "float"); return; - case Basic_f64: ir_write_str_lit(f, "double"); return; + // case Basic_f16: ir_write_str_lit(f, "half"); return; + case Basic_f32: ir_write_str_lit(f, "float"); return; + case Basic_f64: ir_write_str_lit(f, "double"); return; - // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; - case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; - case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; + // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; + case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; + case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; - case Basic_any: ir_write_str_lit(f, "%..any"); return; - case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return; - case Basic_string: ir_write_str_lit(f, "%..string"); return; - case Basic_cstring: ir_write_str_lit(f, "i8*"); return; + case Basic_quaternion128: ir_write_str_lit(f, "%..quaternion128"); return; + case Basic_quaternion256: ir_write_str_lit(f, "%..quaternion256"); return; - case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; + case Basic_any: ir_write_str_lit(f, "%..any"); return; + case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return; + case Basic_string: ir_write_str_lit(f, "%..string"); return; + case Basic_cstring: ir_write_str_lit(f, "i8*"); return; + + case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; } break; @@ -811,6 +814,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * break; } case ExactValue_Complex: { + // xy/ri format type = core_type(type); GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type)); Type *ft = base_complex_elem_type(type); @@ -823,6 +827,26 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_byte(f, '}'); break; } + + case ExactValue_Quaternion: { + // xyzw/ijkr format + type = core_type(type); + GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type)); + Type *ft = base_complex_elem_type(type); + ir_write_byte(f, ' '); + ir_write_byte(f, '{'); + ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft); + ir_write_byte(f, '}'); + break; + } + case ExactValue_Pointer: if (value.value_pointer == 0) { if (is_type_typeid(type)) { @@ -2246,6 +2270,13 @@ void print_llvm_ir(irGen *ir) { ir_print_encoded_local(f, str_lit("..complex128")); ir_write_str_lit(f, " = type {double, double} ; Basic_complex128\n"); + ir_print_encoded_local(f, str_lit("..quaternion64")); + ir_write_str_lit(f, " = type {half, half, half, half} ; Basic_quaternion64\n"); + ir_print_encoded_local(f, str_lit("..quaternion128")); + ir_write_str_lit(f, " = type {float, float, float, float} ; Basic_quaternion128\n"); + ir_print_encoded_local(f, str_lit("..quaternion256")); + ir_write_str_lit(f, " = type {double, double, double, double} ; Basic_quaternion256\n"); + ir_print_encoded_local(f, str_lit("..typeid")); ir_write_str_lit(f, " = type "); ir_print_type(f, m, t_uintptr); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a551f0545..5ba402858 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -745,7 +745,10 @@ exponent: scan_mantissa(t, 10); } - if (t->curr_rune == 'i') { + switch (t->curr_rune) { + case 'i': + case 'j': + case 'k': token.kind = Token_Imag; advance_to_next_rune(t); } diff --git a/src/types.cpp b/src/types.cpp index b5c9152b6..94b89d8f5 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -32,6 +32,9 @@ enum BasicKind { Basic_complex64, Basic_complex128, + Basic_quaternion128, + Basic_quaternion256, + Basic_int, Basic_uint, Basic_uintptr, @@ -66,6 +69,7 @@ enum BasicKind { Basic_UntypedInteger, Basic_UntypedFloat, Basic_UntypedComplex, + Basic_UntypedQuaternion, Basic_UntypedString, Basic_UntypedRune, Basic_UntypedNil, @@ -82,17 +86,18 @@ enum BasicFlag { BasicFlag_Unsigned = GB_BIT(2), BasicFlag_Float = GB_BIT(3), BasicFlag_Complex = GB_BIT(4), - BasicFlag_Pointer = GB_BIT(5), - BasicFlag_String = GB_BIT(6), - BasicFlag_Rune = GB_BIT(7), - BasicFlag_Untyped = GB_BIT(8), + BasicFlag_Quaternion = GB_BIT(5), + BasicFlag_Pointer = GB_BIT(6), + BasicFlag_String = GB_BIT(7), + BasicFlag_Rune = GB_BIT(8), + BasicFlag_Untyped = GB_BIT(9), - BasicFlag_LLVM = GB_BIT(10), + BasicFlag_LLVM = GB_BIT(11), BasicFlag_EndianLittle = GB_BIT(13), BasicFlag_EndianBig = GB_BIT(14), - BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex, + BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex | BasicFlag_Quaternion, BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, BasicFlag_OrderedNumeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Rune, BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, @@ -342,6 +347,9 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_complex64, BasicFlag_Complex, 8, STR_LIT("complex64")}}, {Type_Basic, {Basic_complex128, BasicFlag_Complex, 16, STR_LIT("complex128")}}, + {Type_Basic, {Basic_quaternion128, BasicFlag_Quaternion, 16, STR_LIT("quaternion128")}}, + {Type_Basic, {Basic_quaternion256, BasicFlag_Quaternion, 32, STR_LIT("quaternion256")}}, + {Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}}, {Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}}, {Type_Basic, {Basic_uintptr, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uintptr")}}, @@ -377,6 +385,7 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, {Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}}, {Type_Basic, {Basic_UntypedComplex, BasicFlag_Complex | BasicFlag_Untyped, 0, STR_LIT("untyped complex")}}, + {Type_Basic, {Basic_UntypedQuaternion, BasicFlag_Quaternion | BasicFlag_Untyped, 0, STR_LIT("untyped quaternion")}}, {Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}}, {Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}}, {Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}}, @@ -412,6 +421,9 @@ gb_global Type *t_f64 = &basic_types[Basic_f64]; gb_global Type *t_complex64 = &basic_types[Basic_complex64]; gb_global Type *t_complex128 = &basic_types[Basic_complex128]; +gb_global Type *t_quaternion128 = &basic_types[Basic_quaternion128]; +gb_global Type *t_quaternion256 = &basic_types[Basic_quaternion256]; + gb_global Type *t_int = &basic_types[Basic_int]; gb_global Type *t_uint = &basic_types[Basic_uint]; gb_global Type *t_uintptr = &basic_types[Basic_uintptr]; @@ -446,6 +458,7 @@ gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool]; gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat]; gb_global Type *t_untyped_complex = &basic_types[Basic_UntypedComplex]; +gb_global Type *t_untyped_quaternion = &basic_types[Basic_UntypedQuaternion]; gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString]; gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil]; @@ -472,6 +485,7 @@ gb_global Type *t_type_info_integer = nullptr; gb_global Type *t_type_info_rune = nullptr; gb_global Type *t_type_info_float = nullptr; gb_global Type *t_type_info_complex = nullptr; +gb_global Type *t_type_info_quaternion = nullptr; gb_global Type *t_type_info_any = nullptr; gb_global Type *t_type_info_typeid = nullptr; gb_global Type *t_type_info_string = nullptr; @@ -496,6 +510,7 @@ gb_global Type *t_type_info_integer_ptr = nullptr; gb_global Type *t_type_info_rune_ptr = nullptr; gb_global Type *t_type_info_float_ptr = nullptr; gb_global Type *t_type_info_complex_ptr = nullptr; +gb_global Type *t_type_info_quaternion_ptr = nullptr; gb_global Type *t_type_info_any_ptr = nullptr; gb_global Type *t_type_info_typeid_ptr = nullptr; gb_global Type *t_type_info_string_ptr = nullptr; @@ -924,6 +939,13 @@ bool is_type_complex(Type *t) { } return false; } +bool is_type_quaternion(Type *t) { + t = core_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Quaternion) != 0; + } + return false; +} bool is_type_f32(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { @@ -1063,12 +1085,15 @@ bool is_type_simple_compare(Type *t) { Type *base_complex_elem_type(Type *t) { t = core_type(t); - if (is_type_complex(t)) { + if (t->kind == Type_Basic) { switch (t->Basic.kind) { - // case Basic_complex32: return t_f16; - case Basic_complex64: return t_f32; - case Basic_complex128: return t_f64; - case Basic_UntypedComplex: return t_untyped_float; + // case Basic_complex32: return t_f16; + case Basic_complex64: return t_f32; + case Basic_complex128: return t_f64; + case Basic_quaternion128: return t_f32; + case Basic_quaternion256: return t_f64; + case Basic_UntypedComplex: return t_untyped_float; + case Basic_UntypedQuaternion: return t_untyped_float; } } GB_PANIC("Invalid complex type"); @@ -1818,6 +1843,7 @@ Type *default_type(Type *type) { case Basic_UntypedInteger: return t_int; case Basic_UntypedFloat: return t_f64; case Basic_UntypedComplex: return t_complex128; + case Basic_UntypedQuaternion: return t_quaternion256; case Basic_UntypedString: return t_string; case Basic_UntypedRune: return t_rune; } @@ -2358,6 +2384,8 @@ i64 type_align_of_internal(Type *t, TypePath *path) { case Basic_complex64: case Basic_complex128: return type_size_of_internal(t, path) / 2; + case Basic_quaternion128: case Basic_quaternion256: + return type_size_of_internal(t, path) / 4; } } break; -- cgit v1.2.3 From f4f6e9ad49d6992712815bc7b2e94c8333f33523 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 25 Sep 2019 21:07:56 +0100 Subject: Fix -debug crash on windows caused by missing debug info for files. --- core/os/os_windows.odin | 2 +- src/ir.cpp | 13 ++++++++++++- src/ir_print.cpp | 6 ++++-- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'src/ir_print.cpp') diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin index 9618f83be..e45cf9f5f 100644 --- a/core/os/os_windows.odin +++ b/core/os/os_windows.odin @@ -322,4 +322,4 @@ is_windows_8_1 :: proc() -> bool { is_windows_10 :: proc() -> bool { osvi := get_windows_version_ansi(); return (osvi.major_version == 10 && osvi.minor_version == 0); -} \ No newline at end of file +} diff --git a/src/ir.cpp b/src/ir.cpp index 1c326359b..758267c24 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10028,7 +10028,11 @@ void ir_init_module(irModule *m, Checker *c) { { irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompileUnit); - di->CompileUnit.file = m->info->files.entries[0].value; // Zeroth is the init file + + GB_ASSERT(m->info->files.entries.count > 0); + AstFile *file = m->info->files.entries[0].value; + + di->CompileUnit.file = file; // Zeroth is the init file di->CompileUnit.producer = str_lit("odin"); map_set(&m->debug_info, hash_pointer(m), di); @@ -10047,6 +10051,13 @@ void ir_init_module(irModule *m, Checker *c) { array_init(&m->debug_location_stack, heap_allocator()); // TODO(lachsinc): ir_allocator() ?? } + + { + for_array(i, m->info->files.entries) { + AstFile *file = m->info->files.entries[i].value; + ir_add_debug_info_file(m, file); + } + } } void ir_destroy_module(irModule *m) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 30ad914a9..9414a2338 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -2450,11 +2450,13 @@ void print_llvm_ir(irGen *ir) { for_array(di_index, m->debug_info.entries) { irDebugInfo *di = m->debug_info.entries[di_index].value; + GB_ASSERT_MSG(di != nullptr, "Invalid irDebugInfo"); ir_fprintf(f, "!%d = ", di->id); - switch (di->kind) { case irDebugInfo_CompileUnit: { - irDebugInfo *file = *map_get(&m->debug_info, hash_pointer(di->CompileUnit.file)); + irDebugInfo **found = map_get(&m->debug_info, hash_pointer(di->CompileUnit.file)); + GB_ASSERT_MSG(found != nullptr, "Missing debug info for: %.*s\n", LIT(di->CompileUnit.file->fullpath)); + irDebugInfo *file = *found; ir_fprintf(f, "distinct !DICompileUnit(" "language: DW_LANG_C_plus_plus" // Is this good enough? -- cgit v1.2.3 From 66ae4e5afc87e4804cf371b49cc68cbecf253373 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 1 Oct 2019 20:38:50 +0100 Subject: Change ODIN_OS string for osx from "osx" to "darwin" to allow for other platforms --- core/os/os_darwin.odin | 295 +++++++++++++++++++++++++++++++++++++++++++++ core/time/time_darwin.odin | 3 + core/time/time_osx.odin | 3 - src/build_settings.cpp | 16 +-- src/check_type.cpp | 2 +- src/ir_print.cpp | 14 ++- 6 files changed, 316 insertions(+), 17 deletions(-) create mode 100644 core/os/os_darwin.odin create mode 100644 core/time/time_darwin.odin delete mode 100644 core/time/time_osx.odin (limited to 'src/ir_print.cpp') diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin new file mode 100644 index 000000000..7b183b3aa --- /dev/null +++ b/core/os/os_darwin.odin @@ -0,0 +1,295 @@ +package os + +foreign import dl "system:dl" +foreign import libc "system:c" + +import "core:runtime" +import "core:strings" + +OS :: "darwin"; + +Handle :: distinct i32; +File_Time :: distinct u64; +Errno :: distinct int; + +INVALID_HANDLE :: ~Handle(0); + +O_RDONLY :: 0x00000; +O_WRONLY :: 0x00001; +O_RDWR :: 0x00002; +O_CREATE :: 0x00040; +O_EXCL :: 0x00080; +O_NOCTTY :: 0x00100; +O_TRUNC :: 0x00200; +O_NONBLOCK :: 0x00800; +O_APPEND :: 0x00400; +O_SYNC :: 0x01000; +O_ASYNC :: 0x02000; +O_CLOEXEC :: 0x80000; + + +SEEK_SET :: 0; +SEEK_CUR :: 1; +SEEK_END :: 2; +SEEK_DATA :: 3; +SEEK_HOLE :: 4; +SEEK_MAX :: SEEK_HOLE; + + + +// NOTE(zangent): These are OS specific! +// Do not mix these up! +RTLD_LAZY :: 0x1; +RTLD_NOW :: 0x2; +RTLD_LOCAL :: 0x4; +RTLD_GLOBAL :: 0x8; +RTLD_NODELETE :: 0x80; +RTLD_NOLOAD :: 0x10; +RTLD_FIRST :: 0x100; + + +// "Argv" arguments converted to Odin strings +args := _alloc_command_line_arguments(); + +_File_Time :: struct { + seconds: i64, + nanoseconds: i64, +} + +Stat :: struct { + device_id: i32, // ID of device containing file + mode: u16, // Mode of the file + nlink: u16, // Number of hard links + serial: u64, // File serial number + uid: u32, // User ID of the file's owner + gid: u32, // Group ID of the file's group + rdev: i32, // Device ID, if device + + last_access: File_Time, // Time of last access + modified: File_Time, // Time of last modification + status_change: File_Time, // Time of last status change + created: File_Time, // Time of creation + + size: i64, // Size of the file, in bytes + blocks: i64, // Number of blocks allocated for the file + block_size: i32, // Optimal blocksize for I/O + flags: u32, // User-defined flags for the file + gen_num: u32, // File generation number ..? + _spare: i32, // RESERVED + _reserve1, + _reserve2: i64, // RESERVED +}; + +// File type +S_IFMT :: 0o170000; // Type of file mask +S_IFIFO :: 0o010000; // Named pipe (fifo) +S_IFCHR :: 0o020000; // Character special +S_IFDIR :: 0o040000; // Directory +S_IFBLK :: 0o060000; // Block special +S_IFREG :: 0o100000; // Regular +S_IFLNK :: 0o120000; // Symbolic link +S_IFSOCK :: 0o140000; // Socket + +// File mode +// Read, write, execute/search by owner +S_IRWXU :: 0o0700; // RWX mask for owner +S_IRUSR :: 0o0400; // R for owner +S_IWUSR :: 0o0200; // W for owner +S_IXUSR :: 0o0100; // X for owner + +// Read, write, execute/search by group +S_IRWXG :: 0o0070; // RWX mask for group +S_IRGRP :: 0o0040; // R for group +S_IWGRP :: 0o0020; // W for group +S_IXGRP :: 0o0010; // X for group + +// Read, write, execute/search by others +S_IRWXO :: 0o0007; // RWX mask for other +S_IROTH :: 0o0004; // R for other +S_IWOTH :: 0o0002; // W for other +S_IXOTH :: 0o0001; // X for other + +S_ISUID :: 0o4000; // Set user id on execution +S_ISGID :: 0o2000; // Set group id on execution +S_ISVTX :: 0o1000; // Directory restrcted delete + +S_ISLNK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK; +S_ISREG :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG; +S_ISDIR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR; +S_ISCHR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR; +S_ISBLK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK; +S_ISFIFO :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO; +S_ISSOCK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK; + +R_OK :: 4; // Test for read permission +W_OK :: 2; // Test for write permission +X_OK :: 1; // Test for execute permission +F_OK :: 0; // Test for file existance + +foreign libc { + @(link_name="open") _unix_open :: proc(path: cstring, flags: int, #c_vararg mode: ..any) -> Handle ---; + @(link_name="close") _unix_close :: proc(handle: Handle) ---; + @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---; + @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---; + @(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---; + @(link_name="gettid") _unix_gettid :: proc() -> u64 ---; + @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---; + @(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---; + + @(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---; + @(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---; + @(link_name="free") _unix_free :: proc(ptr: rawptr) ---; + @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---; + @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---; + + @(link_name="exit") _unix_exit :: proc(status: int) ---; +} + +foreign dl { + @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---; + @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---; + @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---; + @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---; +} + +open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { + cstr := strings.clone_to_cstring(path); + handle := _unix_open(cstr, flags, mode); + delete(cstr); + if handle == -1 { + return INVALID_HANDLE, 1; + } + return handle, 0; +} + +close :: proc(fd: Handle) { + _unix_close(fd); +} + +write :: proc(fd: Handle, data: []u8) -> (int, Errno) { + assert(fd != -1); + + bytes_written := _unix_write(fd, &data[0], len(data)); + if(bytes_written == -1) { + return 0, 1; + } + return bytes_written, 0; +} + +read :: proc(fd: Handle, data: []u8) -> (int, Errno) { + assert(fd != -1); + + bytes_read := _unix_read(fd, &data[0], len(data)); + if bytes_read == -1 { + return 0, 1; + } + return bytes_read, 0; +} + +seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { + assert(fd != -1); + + final_offset := i64(_unix_lseek(fd, int(offset), whence)); + if final_offset == -1 { + return 0, 1; + } + return final_offset, 0; +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + prev, _ := seek(fd, 0, SEEK_CUR); + size, err := seek(fd, 0, SEEK_END); + seek(fd, prev, SEEK_SET); + return i64(size), err; +} + + + +// NOTE(bill): Uses startup to initialize it +stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE); +stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE); +stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE); + +/* TODO(zangent): Implement these! +last_write_time :: proc(fd: Handle) -> File_Time {} +last_write_time_by_name :: proc(name: string) -> File_Time {} +*/ + +is_path_separator :: proc(r: rune) -> bool { + return r == '/'; +} + +stat :: inline proc(path: string) -> (Stat, bool) { + s: Stat; + cstr := strings.clone_to_cstring(path); + defer delete(cstr); + ret_int := _unix_stat(cstr, &s); + return s, ret_int==0; +} + +access :: inline proc(path: string, mask: int) -> bool { + cstr := strings.clone_to_cstring(path); + defer delete(cstr); + return _unix_access(cstr, mask) == 0; +} + +heap_alloc :: inline proc(size: int) -> rawptr { + assert(size > 0); + return _unix_calloc(1, size); +} +heap_resize :: inline proc(ptr: rawptr, new_size: int) -> rawptr { + return _unix_realloc(ptr, new_size); +} +heap_free :: inline proc(ptr: rawptr) { + _unix_free(ptr); +} + +getenv :: proc(name: string) -> (string, bool) { + path_str := strings.clone_to_cstring(name); + defer delete(path_str); + cstr := _unix_getenv(path_str); + if cstr == nil { + return "", false; + } + return string(cstr), true; +} + +exit :: inline proc(code: int) -> ! { + _unix_exit(code); +} + + +current_thread_id :: proc "contextless" () -> int { + // return int(_unix_gettid()); + return 0; +} + +dlopen :: inline proc(filename: string, flags: int) -> rawptr { + cstr := strings.clone_to_cstring(filename); + defer delete(cstr); + handle := _unix_dlopen(cstr, flags); + return handle; +} +dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr { + assert(handle != nil); + cstr := strings.clone_to_cstring(symbol); + defer delete(cstr); + proc_handle := _unix_dlsym(handle, cstr); + return proc_handle; +} +dlclose :: inline proc(handle: rawptr) -> bool { + assert(handle != nil); + return _unix_dlclose(handle) == 0; +} +dlerror :: proc() -> string { + return string(_unix_dlerror()); +} + + +_alloc_command_line_arguments :: proc() -> []string { + res := make([]string, len(runtime.args__)); + for arg, i in runtime.args__ { + res[i] = string(arg); + } + return res; +} diff --git a/core/time/time_darwin.odin b/core/time/time_darwin.odin new file mode 100644 index 000000000..7d1a1441c --- /dev/null +++ b/core/time/time_darwin.odin @@ -0,0 +1,3 @@ +package time + +IS_SUPPORTED :: false; \ No newline at end of file diff --git a/core/time/time_osx.odin b/core/time/time_osx.odin deleted file mode 100644 index 7d1a1441c..000000000 --- a/core/time/time_osx.odin +++ /dev/null @@ -1,3 +0,0 @@ -package time - -IS_SUPPORTED :: false; \ No newline at end of file diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 956ffe1ed..b4abf41a7 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2,7 +2,7 @@ enum TargetOsKind { TargetOs_Invalid, TargetOs_windows, - TargetOs_osx, + TargetOs_darwin, TargetOs_linux, TargetOs_essence, @@ -30,7 +30,7 @@ enum TargetEndianKind { String target_os_names[TargetOs_COUNT] = { str_lit(""), str_lit("windows"), - str_lit("osx"), + str_lit("darwin"), str_lit("linux"), str_lit("essence"), }; @@ -166,8 +166,8 @@ gb_global TargetMetrics target_linux_amd64 = { str_lit("x86_64-pc-linux-gnu"), }; -gb_global TargetMetrics target_osx_amd64 = { - TargetOs_osx, +gb_global TargetMetrics target_darwin_amd64 = { + TargetOs_darwin, TargetArch_amd64, 8, 16, @@ -189,7 +189,7 @@ struct NamedTargetMetrics { gb_global NamedTargetMetrics named_targets[] = { { str_lit("essence_amd64"), &target_essence_amd64 }, - { str_lit("macos_amd64"), &target_osx_amd64 }, + { str_lit("darwin_amd64"), &target_darwin_amd64 }, { str_lit("linux_386"), &target_linux_386 }, { str_lit("linux_amd64"), &target_linux_amd64 }, { str_lit("windows_386"), &target_windows_386 }, @@ -566,7 +566,7 @@ void init_build_context(TargetMetrics *cross_target) { #if defined(GB_SYSTEM_WINDOWS) metrics = target_windows_amd64; #elif defined(GB_SYSTEM_OSX) - metrics = target_osx_amd64; + metrics = target_darwin_amd64; #else metrics = target_linux_amd64; #endif @@ -618,7 +618,7 @@ void init_build_context(TargetMetrics *cross_target) { case TargetOs_windows: bc->link_flags = str_lit("/machine:x64 "); break; - case TargetOs_osx: + case TargetOs_darwin: break; case TargetOs_linux: bc->link_flags = str_lit("-arch x86-64 "); @@ -631,7 +631,7 @@ void init_build_context(TargetMetrics *cross_target) { case TargetOs_windows: bc->link_flags = str_lit("/machine:x86 "); break; - case TargetOs_osx: + case TargetOs_darwin: gb_printf_err("Unsupported architecture\n"); gb_exit(1); break; diff --git a/src/check_type.cpp b/src/check_type.cpp index 465c11731..9f5ed0ed3 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1915,7 +1915,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall } } } else if (build_context.ODIN_OS == "linux" || - build_context.ODIN_OS == "osx") { + build_context.ODIN_OS == "darwin") { Type *bt = core_type(original_type); switch (bt->kind) { // Okay to pass by value (usually) diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 9414a2338..db9eb1bfb 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1165,7 +1165,8 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven switch (cc) { case ProcCC_Odin: ir_write_str_lit(f, ""); break; case ProcCC_Contextless: ir_write_str_lit(f, ""); break; - case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break; + // case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break; + case ProcCC_CDecl: ir_write_str_lit(f, ""); break; case ProcCC_StdCall: ir_write_str_lit(f, "cc 64 "); break; case ProcCC_FastCall: ir_write_str_lit(f, "cc 65 "); break; case ProcCC_None: ir_write_str_lit(f, ""); break; @@ -1175,8 +1176,8 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven void ir_print_context_parameter_prefix(irFileBuffer *f, irModule *m) { ir_print_type(f, m, t_context_ptr); - ir_write_str_lit(f, " noalias nonnull nocapture inreg "); - // ir_write_str_lit(f, " noalias nonnull nocapture "); + // ir_write_str_lit(f, " noalias nonnull nocapture inreg "); + ir_write_str_lit(f, " noalias nonnull nocapture "); } void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { @@ -1227,6 +1228,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_write_str_lit(f, ", "); ir_print_type(f, m, type); ir_fprintf(f, "* %%%d, align 1", instr->ZeroInit.address->index); + // ir_fprintf(f, "* %%%d", instr->ZeroInit.address->index); break; } @@ -2244,9 +2246,11 @@ void print_llvm_ir(irGen *ir) { defer (ir_file_buffer_destroy(f)); i32 word_bits = cast(i32)(8*build_context.word_size); - if (build_context.ODIN_OS == "osx" || build_context.ODIN_OS == "macos") { + if (build_context.ODIN_OS == "darwin") { GB_ASSERT(word_bits == 64); - ir_write_str_lit(f, "target triple = \"x86_64-apple-macosx10.8\"\n\n"); + ir_write_str_lit(f, "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"); + ir_write_str_lit(f, "target triple = \"x86_64-apple-macosx10.8\"\n"); + ir_write_str_lit(f, "\n"); } else if (build_context.ODIN_OS == "windows") { ir_fprintf(f, "target triple = \"x86%s-pc-windows-msvc\"\n\n", word_bits == 64 ? "_64" : ""); if (word_bits == 64 && build_context.metrics.arch == TargetArch_amd64) { -- cgit v1.2.3 From 5b52fed268b3544bea562f4263d447b96ebacd3a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 10 Oct 2019 20:41:16 +0100 Subject: Correct (experimental) System V AMD64 ABI support --- src/check_type.cpp | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/ir.cpp | 111 ++++++++++++++++++++++++++-------- src/ir_print.cpp | 84 ++++++++++++++++++-------- 3 files changed, 317 insertions(+), 52 deletions(-) (limited to 'src/ir_print.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 65e0bc880..fe5825c47 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1837,6 +1837,168 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { return tuple; } +Array systemv_distribute_struct_fields(Type *t, i32 level=0) { + t = base_type(t); + GB_ASSERT_MSG(t->kind == Type_Struct, "%s %d", type_to_string(t), level); + TypeStruct *ts = &t->Struct; + + auto distributed = array_make(heap_allocator(), 0, ts->fields.count); + + for_array(field_index, ts->fields) { + Entity *f = ts->fields[field_index]; + Type *bt = core_type(f->type); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + goto DEFAULT; + } else { + // IMPORTANT TOOD(bill): handle #packed structs correctly + // IMPORTANT TODO(bill): handle #align structs correctly + auto nested = systemv_distribute_struct_fields(f->type, level+1); + for_array(i, nested) { + array_add(&distributed, nested[i]); + } + array_free(&nested); + } + break; + + case Type_Array: + for (i64 i = 0; i < bt->Array.count; i++) { + array_add(&distributed, bt->Array.elem); + } + break; + + case Type_BitSet: + array_add(&distributed, bit_set_to_int(bt)); + break; + + case Type_Tuple: + GB_PANIC("Invalid struct field type"); + break; + + case Type_Slice: + case Type_DynamicArray: + case Type_Map: + case Type_Union: + case Type_BitField: // TODO(bill): Ignore? + // NOTE(bill, 2019-10-10): Odin specific, don't worry about C calling convention yet + goto DEFAULT; + + case Type_Pointer: + case Type_Proc: + case Type_SimdVector: // TODO(bill): Is this correct logic? + default: + DEFAULT:; + if (type_size_of(bt) > 0) { + array_add(&distributed, bt); + } + break; + } + } + + return distributed; +} + +Type *struct_type_from_systemv_distribute_struct_fields(Type *abi_type) { + GB_ASSERT(is_type_tuple(abi_type)); + Type *final_type = alloc_type_struct(); + final_type->Struct.fields = abi_type->Tuple.variables; + return final_type; +} + + +Type *handle_single_distributed_type_parameter(Array const &types, bool packed, isize *offset) { + GB_ASSERT(types.count > 0); + + if (types.count == 1) { + if (offset) *offset = 1; + if (is_type_float(types[0])) { + return types[0]; + } else if (type_size_of(types[0]) == 8) { + return types[0]; + } else { + return t_u64; + } + } else if (types.count >= 2) { + if (types[0] == t_f32 && types[1] == t_f32) { + if (offset) *offset = 2; + return alloc_type_simd_vector(2, t_f32); + } else if (type_size_of(types[0]) == 8) { + if (offset) *offset = 1; + return types[0]; + } + + i64 total_size = 0; + isize i = 0; + if (packed) { + for (; i < types.count && total_size < 8; i += 1) { + Type *t = types[i]; + i64 s = type_size_of(t); + total_size += s; + } + } else { + for (; i < types.count && total_size < 8; i += 1) { + Type *t = types[i]; + i64 s = gb_max(type_size_of(t), 0); + i64 a = gb_max(type_align_of(t), 1); + isize ts = align_formula(total_size, a); + if (ts >= 8) { + break; + } + total_size = ts + s; + } + } + if (offset) *offset = i; + return t_u64; + } + + return nullptr; +} + +Type *handle_struct_system_v_amd64_abi_type(Type *t) { + GB_ASSERT(is_type_struct(t)); + Type *bt = core_type(t); + i64 size = type_size_of(bt); + + if (!bt->Struct.is_raw_union) { + auto field_types = systemv_distribute_struct_fields(bt); + defer (array_free(&field_types)); + + GB_ASSERT(field_types.count <= 16); + + Type *final_type = nullptr; + + if (field_types.count == 0) { + // Do nothing + } else if (field_types.count == 1) { + final_type = field_types[0]; + } else { + if (size <= 8) { + isize offset = 0; + final_type = handle_single_distributed_type_parameter(field_types, bt->Struct.is_packed, &offset); + } else { + isize offset = 0; + isize next_offset = 0; + Type *two_types[2] = {}; + + two_types[0] = handle_single_distributed_type_parameter(field_types, bt->Struct.is_packed, &offset); + auto remaining = array_slice(field_types, offset, field_types.count); + two_types[1] = handle_single_distributed_type_parameter(remaining, bt->Struct.is_packed, &next_offset); + GB_ASSERT(offset + next_offset == field_types.count); + + auto variables = array_make(heap_allocator(), 2); + variables[0] = alloc_entity_param(nullptr, empty_token, two_types[0], false, false); + variables[1] = alloc_entity_param(nullptr, empty_token, two_types[1], false, false); + final_type = alloc_type_tuple(); + final_type->Tuple.variables = variables; + } + } + return final_type; + } else { + return t; + } +} + Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc) { Type *new_type = original_type; @@ -1901,6 +2063,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall { i64 align = type_align_of(original_type); i64 size = type_size_of(original_type); + switch (8*size) { case 8: new_type = t_u8; break; case 16: new_type = t_u16; break; @@ -1944,6 +2107,15 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall i64 size = type_size_of(original_type); if (8*size > 16) { new_type = alloc_type_pointer(original_type); + } else if (build_context.ODIN_ARCH == "amd64") { + // NOTE(bill): System V AMD64 ABI + if (bt->Struct.is_raw_union) { + // TODO(bill): Handle raw union correctly for + break; + } + + new_type = handle_struct_system_v_amd64_abi_type(bt); + return new_type; } break; @@ -2018,7 +2190,7 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal } } else if (build_context.ODIN_OS == "linux" || build_context.ODIN_OS == "darwin") { if (build_context.ODIN_ARCH == "amd64") { - + } } else { // IMPORTANT TODO(bill): figure out the ABI settings for Linux, OSX etc. for diff --git a/src/ir.cpp b/src/ir.cpp index f29d389f4..16a017407 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -147,6 +147,7 @@ struct irProcedure { Array context_stack; + i32 parameter_count; irValue *return_ptr_hint_value; Ast * return_ptr_hint_ast; @@ -425,6 +426,7 @@ enum irParamPasskind { irParamPass_Integer, // Pass as an integer of the same size irParamPass_ConstRef, // Pass as a pointer but the value is immutable irParamPass_BitCast, // Pass by value and bit cast to the correct type + irParamPass_Tuple, // Pass across multiple parameters (System V AMD64, up to 2) }; struct irValueParam { @@ -433,6 +435,7 @@ struct irValueParam { Entity * entity; Type * type; Type * original_type; + i32 index; Array referrers; }; @@ -914,15 +917,18 @@ irValue *ir_value_global(Entity *e, irValue *value) { if (value) value->uses += 1; return v; } -irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type) { +irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type, i32 index) { irValue *v = ir_alloc_value(irValue_Param); v->Param.kind = irParamPass_Value; v->Param.parent = parent; - v->Param.entity = e; - v->Param.original_type = e->type; + if (e != nullptr) { + v->Param.entity = e; + v->Param.original_type = e->type; + } v->Param.type = abi_type; + v->Param.index = index; - if (abi_type != e->type) { + if (e != nullptr && abi_type != e->type) { if (is_type_pointer(abi_type)) { GB_ASSERT(e->kind == Entity_Variable); v->Param.kind = irParamPass_Pointer; @@ -935,8 +941,12 @@ irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type) { v->Param.kind = irParamPass_Value; } else if (is_type_simd_vector(abi_type)) { v->Param.kind = irParamPass_BitCast; + } else if (is_type_float(abi_type)) { + v->Param.kind = irParamPass_BitCast; + } else if (is_type_tuple(abi_type)) { + v->Param.kind = irParamPass_Tuple; } else { - GB_PANIC("Invalid abi type pass kind"); + GB_PANIC("Invalid abi type pass kind %s", type_to_string(abi_type)); } } array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here @@ -1429,7 +1439,7 @@ irValue *ir_value_procedure(irModule *m, Entity *entity, Type *type, Ast *type_e Type *t = base_type(type); GB_ASSERT(is_type_proc(t)); - array_init(&v->Proc.params, ir_allocator(), 0, t->Proc.param_count); + array_init(&v->Proc.params, heap_allocator(), 0, t->Proc.param_count); return v; } @@ -1499,7 +1509,9 @@ void ir_start_block(irProcedure *proc, irBlock *block) { } - +irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t); +irValue *ir_address_from_load_or_generate_local(irProcedure *proc, irValue *val); +irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index); @@ -1713,9 +1725,12 @@ irValue *ir_add_global_generated(irModule *m, Type *type, irValue *value) { irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i32 index) { - irValue *v = ir_value_param(proc, e, abi_type); + irValue *v = ir_value_param(proc, e, abi_type, index); + array_add(&proc->params, v); irValueParam *p = &v->Param; + irValue *res = nullptr; + ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope, e); defer (ir_pop_debug_location(proc->module)); @@ -1750,6 +1765,24 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i ir_emit_store(proc, l, x); return x; } + case irParamPass_Tuple: { + irValue *l = ir_add_local(proc, e, expr, true, index); + Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); + irValue *ptr = ir_emit_bitcast(proc, l, alloc_type_pointer(st)); + if (abi_type->Tuple.variables.count > 0) { + array_pop(&proc->params); + } + for_array(i, abi_type->Tuple.variables) { + Type *t = abi_type->Tuple.variables[i]->type; + + irValue *elem = ir_value_param(proc, nullptr, t, index+cast(i32)i); + array_add(&proc->params, elem); + + irValue *dst = ir_emit_struct_ep(proc, ptr, cast(i32)i); + ir_emit_store(proc, dst, elem); + } + return ir_emit_load(proc, l); + } } @@ -2945,10 +2978,6 @@ void ir_emit_unreachable(irProcedure *proc) { ir_emit(proc, ir_instr_unreachable(proc)); } -irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t); -irValue *ir_address_from_load_or_generate_local(irProcedure *proc, irValue *val); -irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index); - irValue *ir_get_package_value(irModule *m, String package_name, String entity_name) { AstPackage *rt_pkg = get_core_package(m->info, package_name); @@ -2998,7 +3027,7 @@ Array ir_value_to_array(irProcedure *p, irValue *value) { } -irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false) { +irValue *ir_emit_call(irProcedure *p, irValue *value, Array const &args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false) { Type *pt = base_type(ir_type(value)); GB_ASSERT(pt->kind == Type_Proc); Type *results = pt->Proc.results; @@ -3008,6 +3037,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, Pro context_ptr = ir_find_or_generate_context_ptr(p); } + bool is_c_vararg = pt->Proc.c_vararg; isize param_count = pt->Proc.param_count; if (is_c_vararg) { @@ -3016,9 +3046,13 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, Pro } else { GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count); } + + auto processed_args = array_make(heap_allocator(), 0, args.count); + for (isize i = 0; i < param_count; i++) { Entity *e = pt->Proc.params->Tuple.variables[i]; if (e->kind != Entity_Variable) { + array_add(&processed_args, args[i]); continue; } GB_ASSERT(e->flags & EntityFlag_Param); @@ -3027,20 +3061,29 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, Pro Type *new_type = pt->Proc.abi_compat_params[i]; Type *arg_type = ir_type(args[i]); if (are_types_identical(arg_type, new_type)) { + array_add(&processed_args, args[i]); // NOTE(bill): Done } else if (!are_types_identical(original_type, new_type)) { if (is_type_pointer(new_type) && !is_type_pointer(original_type)) { if (e->flags&EntityFlag_ImplicitReference) { - args[i] = ir_address_from_load_or_generate_local(p, args[i]); + array_add(&processed_args, ir_address_from_load_or_generate_local(p, args[i])); } else if (!is_type_pointer(arg_type)) { - args[i] = ir_copy_value_to_ptr(p, args[i], original_type, 16); + array_add(&processed_args, ir_copy_value_to_ptr(p, args[i], original_type, 16)); } } else if (is_type_integer(new_type)) { - args[i] = ir_emit_transmute(p, args[i], new_type); + array_add(&processed_args, ir_emit_transmute(p, args[i], new_type)); } else if (new_type == t_llvm_bool) { - args[i] = ir_emit_conv(p, args[i], new_type); + array_add(&processed_args, ir_emit_conv(p, args[i], new_type)); } else if (is_type_simd_vector(new_type)) { - args[i] = ir_emit_bitcast(p, args[i], new_type); + array_add(&processed_args, ir_emit_bitcast(p, args[i], new_type)); + } else if (is_type_tuple(new_type)) { + Type *abi_type = pt->Proc.abi_compat_params[i]; + Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); + irValue *x = ir_emit_transmute(p, args[i], st); + for (isize j = 0; j < new_type->Tuple.variables.count; j++) { + irValue *xx = ir_emit_struct_ev(p, x, cast(i32)j); + array_add(&processed_args, xx); + } } } } @@ -3066,10 +3109,10 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array args, Pro return_ptr = ir_add_local_generated(p, rt, true); } GB_ASSERT(is_type_pointer(ir_type(return_ptr))); - ir_emit(p, ir_instr_call(p, value, return_ptr, args, nullptr, context_ptr, inlining)); + ir_emit(p, ir_instr_call(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining)); result = ir_emit_load(p, return_ptr); } else { - result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining)); + result = ir_emit(p, ir_instr_call(p, value, nullptr, processed_args, abi_rt, context_ptr, inlining)); if (abi_rt != results) { result = ir_emit_transmute(p, result, rt); } @@ -9540,6 +9583,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { //////////////////////////////////////////////////////////////// void ir_number_proc_registers(irProcedure *proc) { + // i32 reg_index = proc->parameter_count; i32 reg_index = 0; for_array(i, proc->blocks) { irBlock *b = proc->blocks[i]; @@ -9595,13 +9639,15 @@ void ir_begin_procedure_body(irProcedure *proc) { proc->entry_block = ir_new_block(proc, proc->type_expr, "entry"); ir_start_block(proc, proc->entry_block); + i32 parameter_index = 0; + if (proc->type->Proc.return_by_pointer) { // NOTE(bill): this must be the first parameter stored Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(proc->type->Proc.results)); Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; - irValue *param = ir_value_param(proc, e, ptr_type); + irValue *param = ir_value_param(proc, e, ptr_type, -1); param->Param.kind = irParamPass_Pointer; ir_module_add_value(proc->module, e, param); @@ -9630,13 +9676,19 @@ void ir_begin_procedure_body(irProcedure *proc) { Entity *e = params->variables[i]; if (e->kind != Entity_Variable) { + parameter_index += 1; continue; } Type *abi_type = proc->type->Proc.abi_compat_params[i]; if (e->token.string != "" && !is_blank_ident(e->token)) { - irValue *param = ir_add_param(proc, e, name, abi_type, cast(i32)(i+1)); - array_add(&proc->params, param); + ir_add_param(proc, e, name, abi_type, parameter_index); + } + + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; + } else { + parameter_index += 1; } } } else { @@ -9645,6 +9697,7 @@ void ir_begin_procedure_body(irProcedure *proc) { for_array(i, params->variables) { Entity *e = params->variables[i]; if (e->kind != Entity_Variable) { + parameter_index += 1; continue; } Type *abi_type = e->type; @@ -9652,8 +9705,12 @@ void ir_begin_procedure_body(irProcedure *proc) { abi_type = abi_types[i]; } if (e->token.string != "" && !is_blank_ident(e->token)) { - irValue *param = ir_add_param(proc, e, nullptr, abi_type, cast(i32)(i+1)); - array_add(&proc->params, param); + ir_add_param(proc, e, nullptr, abi_type, parameter_index); + } + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; + } else { + parameter_index += 1; } } } @@ -9695,11 +9752,13 @@ void ir_begin_procedure_body(irProcedure *proc) { if (proc->type->Proc.calling_convention == ProcCC_Odin) { Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; - irValue *param = ir_value_param(proc, e, e->type); + irValue *param = ir_value_param(proc, e, e->type, -1); ir_module_add_value(proc->module, e, param); irContextData ctx = {param, proc->scope_index}; array_add(&proc->context_stack, ctx); } + + proc->parameter_count = parameter_index; } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index db9eb1bfb..f76539fd6 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1130,7 +1130,11 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin break; } case irValue_Param: - ir_print_encoded_local(f, value->Param.entity->token.string); + if (value->Param.index >= 0) { + ir_fprintf(f, "%%_.%d", value->Param.index); + } else { + ir_print_encoded_local(f, value->Param.entity->token.string); + } break; case irValue_SourceCodeLocation: { irValue *file = value->SourceCodeLocation.file; @@ -1936,9 +1940,6 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { } ir_write_byte(f, ' '); irValue *arg = call->args[i]; - if (is_type_boolean(t)) { - - } ir_print_value(f, m, arg, t); param_index++; } @@ -1953,24 +1954,43 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { param_index++; } } else { - GB_ASSERT(call->args.count == params->variables.count); + // GB_ASSERT(call->args.count == params->variables.count); + isize arg_index = 0; for_array(i, params->variables) { Entity *e = params->variables[i]; GB_ASSERT(e != nullptr); - if (e->kind != Entity_Variable) continue; + if (e->kind != Entity_Variable) { + arg_index++; + continue; + } if (param_index > 0) ir_write_str_lit(f, ", "); - irValue *arg = call->args[i]; Type *t = proc_type->Proc.abi_compat_params[i]; - - ir_print_type(f, m, t); - if (e->flags&EntityFlag_NoAlias) { - ir_write_str_lit(f, " noalias"); + if (is_type_tuple(t)) { + for_array(j, t->Tuple.variables) { + if (j > 0) ir_write_str_lit(f, ", "); + + irValue *arg = call->args[arg_index++]; + + ir_print_type(f, m, t->Tuple.variables[j]->type); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + ir_write_byte(f, ' '); + ir_print_value(f, m, arg, t); + param_index++; + } + } else { + irValue *arg = call->args[arg_index++]; + ir_print_type(f, m, t); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + ir_write_byte(f, ' '); + ir_print_value(f, m, arg, t); + param_index++; } - ir_write_byte(f, ' '); - ir_print_value(f, m, arg, t); - param_index++; } } } @@ -2089,7 +2109,8 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { if (param_count > 0) { TypeTuple *params = &proc_type->params->Tuple; - for (isize i = 0; i < param_count; i++) { + isize parameter_index = 0; + for (isize i = 0; i < param_count; i++, parameter_index++) { Entity *e = params->variables[i]; Type *original_type = e->type; Type *abi_type = proc_type->abi_compat_params[i]; @@ -2099,16 +2120,29 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { if (i+1 == params->variables.count && proc_type->c_vararg) { ir_write_str_lit(f, " ..."); } else { - ir_print_type(f, m, abi_type); - if (e->flags&EntityFlag_NoAlias) { - ir_write_str_lit(f, " noalias"); - } - if (proc->body != nullptr) { - if (e->token.string != "" && !is_blank_ident(e->token)) { - ir_write_byte(f, ' '); - ir_print_encoded_local(f, e->token.string); - } else { - ir_fprintf(f, " %%_.param_%td", i); + if (is_type_tuple(abi_type)) { + for_array(j, abi_type->Tuple.variables) { + if (j > 0) ir_write_string(f, str_lit(", ")); + + Type *tft = abi_type->Tuple.variables[j]->type; + ir_print_type(f, m, tft); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + + if (proc->body != nullptr) { + ir_fprintf(f, " %%_.%td", parameter_index+j); + } + } + parameter_index += abi_type->Tuple.variables.count-1; + param_index += abi_type->Tuple.variables.count-1; + } else { + ir_print_type(f, m, abi_type); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + if (proc->body != nullptr) { + ir_fprintf(f, " %%_.%td", parameter_index); } } } -- cgit v1.2.3 From f12ded54f2b1a390f556e67a17ff0bf4c301a8e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 20 Oct 2019 13:11:28 +0100 Subject: Support for named indices for array-like compound literals `{3 = a, 1 = b}` --- src/check_expr.cpp | 103 +++++++++++++++++++++++++++++++++++++----------- src/ir.cpp | 112 +++++++++++++++++++++++++++++++++++++++-------------- src/ir_print.cpp | 60 +++++++++++++++++++++------- src/parser.hpp | 1 + 4 files changed, 213 insertions(+), 63 deletions(-) (limited to 'src/ir_print.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b2a2528dd..60c057ae9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7066,7 +7066,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type i64 max = 0; - isize index = 0; Type *bet = base_type(elem_type); if (!elem_type_can_be_constant(bet)) { @@ -7077,40 +7076,100 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type break; } - for (; index < cl->elems.count; index++) { - Ast *e = cl->elems[index]; - if (e == nullptr) { - error(node, "Invalid literal element"); - continue; - } + if (cl->elems[0]->kind == Ast_FieldValue) { + if (is_type_simd_vector(t)) { + error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); + } else { + Map seen = {}; + map_init(&seen, heap_allocator()); + defer (map_destroy(&seen)); - if (e->kind == Ast_FieldValue) { - error(e, "'field = value' is only allowed in struct literals"); - continue; - } + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + + Operand op_index = {}; + check_expr(c, &op_index, fv->field); + + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } + + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + + if (map_get(&seen, hash_integer(u64(index))) != nullptr) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } + map_set(&seen, hash_integer(u64(index)), true); + + if (max < index) { + max = index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } - if (0 <= max_type_count && max_type_count <= index) { - error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + cl->max_index = max; } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, e, elem_type); - check_assignment(c, &operand, elem_type, context_name); - is_constant = is_constant && operand.mode == Addressing_Constant; - } - if (max < index) { - max = index; + } else { + isize index = 0; + for (; index < cl->elems.count; index++) { + Ast *e = cl->elems[index]; + if (e == nullptr) { + error(node, "Invalid literal element"); + continue; + } + + if (e->kind == Ast_FieldValue) { + error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + + if (0 <= max_type_count && max_type_count <= index) { + error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + + if (max < index) { + max = index; + } } + if (t->kind == Type_Array) { if (is_to_be_determined_array_count) { t->Array.count = max; - } else if (0 < max && max < t->Array.count) { - error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } else if (cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->Array.count) { + error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } } } + if (t->kind == Type_SimdVector) { if (!is_constant) { error(node, "Expected all constant elements for a simd vector"); diff --git a/src/ir.cpp b/src/ir.cpp index 0bf706f67..bea9ee8b3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1552,6 +1552,7 @@ irValue *ir_add_module_constant(irModule *m, Type *type, ExactValue value) { if (count == 0) { return ir_value_nil(type); } + count = gb_max(cl->max_index+1, count); Type *elem = base_type(type)->Slice.elem; Type *t = alloc_type_array(elem, count); irValue *backing_array = ir_add_module_constant(m, t, value); @@ -7859,13 +7860,29 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - if (ir_is_elem_const(proc->module, elem, et)) { - continue; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (ir_is_elem_const(proc->module, fv->value, et)) { + continue; + } + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + irCompoundLitElemTempData data = {}; + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + + } else { + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + irCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); } - irCompoundLitElemTempData data = {}; - data.expr = elem; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); } for_array(i, temp_data) { @@ -7881,6 +7898,9 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { defer (proc->return_ptr_hint_used = return_ptr_hint_used); Ast *expr = temp_data[i].expr; + if (expr == nullptr) { + continue; + } proc->return_ptr_hint_value = temp_data[i].gep; proc->return_ptr_hint_ast = unparen_expr(expr); @@ -7918,18 +7938,40 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - if (ir_is_elem_const(proc->module, elem, et)) { - continue; - } - irValue *field_expr = ir_build_expr(proc, elem); - Type *t = ir_type(field_expr); - GB_ASSERT(t->kind != Type_Tuple); - irValue *ev = ir_emit_conv(proc, field_expr, et); + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); - irCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); + if (ir_is_elem_const(proc->module, fv->value, et)) { + continue; + } + + + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(fv->field->tav.value); + + irValue *field_expr = ir_build_expr(proc, fv->value); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } else { + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + irValue *field_expr = ir_build_expr(proc, elem); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); + } } for_array(i, temp_data) { @@ -7950,28 +7992,42 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (cl->elems.count == 0) { break; } - Type *elem = bt->DynamicArray.elem; + Type *et = bt->DynamicArray.elem; gbAllocator a = ir_allocator(); - irValue *size = ir_const_int(type_size_of(elem)); - irValue *align = ir_const_int(type_align_of(elem)); + irValue *size = ir_const_int(type_size_of(et)); + irValue *align = ir_const_int(type_align_of(et)); + + i64 item_count = gb_max(cl->max_index+1, cl->elems.count); { + auto args = array_make(a, 5); args[0] = ir_emit_conv(proc, v, t_rawptr); args[1] = size; args[2] = align; - args[3] = ir_const_int(2*cl->elems.count); + args[3] = ir_const_int(2*item_count); // TODO(bill): Is this too much waste? args[4] = ir_emit_source_code_location(proc, proc_name, pos); ir_emit_runtime_call(proc, "__dynamic_array_reserve", args); } - i64 item_count = cl->elems.count; - irValue *items = ir_generate_array(proc->module, elem, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); + irValue *items = ir_generate_array(proc->module, et, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); - for_array(field_index, cl->elems) { - Ast *f = cl->elems[field_index]; - irValue *value = ir_emit_conv(proc, ir_build_expr(proc, f), elem); - irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); - ir_emit_store(proc, ep, value); + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + + i64 field_index = exact_value_to_i64(fv->field->tav.value); + + irValue *ev = ir_build_expr(proc, fv->value); + irValue *value = ir_emit_conv(proc, ev, et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); + ir_emit_store(proc, ep, value); + } else { + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, elem), et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)i); + ir_emit_store(proc, ep, value); + } } { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index f76539fd6..cece0c1db 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -876,22 +876,56 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_str_lit(f, "zeroinitializer"); break; } - GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); + if (cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + ir_write_byte(f, '['); + for (i64 i = 0; i < type->Array.count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + + bool found = false; + + for (isize j = 0; j < elem_count; j++) { + Ast *elem = cl->elems[j]; + ast_node(fv, FieldValue, elem); + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + ir_print_compound_element(f, m, tav.value, elem_type); + found = true; + break; + } + } - ir_write_byte(f, '['); + if (!found) { + ir_print_type(f, m, elem_type); + ir_write_byte(f, ' '); + ir_write_str_lit(f, "zeroinitializer"); + } + } + ir_write_byte(f, ']'); + } else { + GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); - for (isize i = 0; i < elem_count; i++) { - if (i > 0) ir_write_str_lit(f, ", "); - TypeAndValue tav = cl->elems[i]->tav; - GB_ASSERT(tav.mode != Addressing_Invalid); - ir_print_compound_element(f, m, tav.value, elem_type); - } - for (isize i = elem_count; i < type->Array.count; i++) { - if (i >= elem_count) ir_write_str_lit(f, ", "); - ir_print_compound_element(f, m, empty_exact_value, elem_type); - } + ir_write_byte(f, '['); - ir_write_byte(f, ']'); + for (isize i = 0; i < elem_count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + ir_print_compound_element(f, m, tav.value, elem_type); + } + for (isize i = elem_count; i < type->Array.count; i++) { + if (i >= elem_count) ir_write_str_lit(f, ", "); + ir_print_compound_element(f, m, empty_exact_value, elem_type); + } + + ir_write_byte(f, ']'); + } } else if (is_type_simd_vector(type)) { ast_node(cl, CompoundLit, value.value_compound); diff --git a/src/parser.hpp b/src/parser.hpp index 419cf9da3..9c3f733e5 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -249,6 +249,7 @@ enum StmtAllowFlag { Ast *type; \ Array elems; \ Token open, close; \ + i64 max_index; \ }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ -- cgit v1.2.3 From 7fae890ef9eb72f3131914f9af2469f9bfb1e59e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 14:06:29 +0100 Subject: Allow ranges for array-like compound literals --- core/odin/parser/parser.odin | 3 + src/check_expr.cpp | 229 ++++++++++++++++++++++++++++++++++--------- src/check_stmt.cpp | 84 +--------------- src/common.cpp | 3 + src/exact_value.cpp | 2 +- src/ir.cpp | 124 ++++++++++++++++++----- src/ir_print.cpp | 48 +++++++-- src/parser.cpp | 11 ++- src/parser.hpp | 1 + 9 files changed, 340 insertions(+), 165 deletions(-) (limited to 'src/ir_print.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index fee9b7bdc..fb3a87e8b 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2391,6 +2391,9 @@ parse_value :: proc(p: ^Parser) -> ^ast.Expr { if p.curr_tok.kind == .Open_Brace { return parse_literal_value(p, nil); } + prev_allow_range = p.allow_range; + defer p.allow_range = prev_allow_range; + p.allow_range = true; return parse_expr(p, false); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ea9248089..f07ebe09c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6635,6 +6635,98 @@ bool ternary_compare_types(Type *x, Type *y) { } +bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_) { + if (!is_ast_range(node)) { + return false; + } + + ast_node(ie, BinaryExpr, node); + + check_expr(c, x, ie->left); + if (x->mode == Addressing_Invalid) { + return false; + } + check_expr(c, y, ie->right); + if (y->mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, x, y->type); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, x->type); + if (y->mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, x, default_type(y->type)); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, default_type(x->type)); + if (y->mode == Addressing_Invalid) { + return false; + } + + if (!are_types_identical(x->type, y->type)) { + if (x->type != t_invalid && + y->type != t_invalid) { + gbString xt = type_to_string(x->type); + gbString yt = type_to_string(y->type); + gbString expr_str = expr_to_string(x->expr); + error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + return false; + } + + Type *type = x->type; + if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); + return false; + } + + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + ExactValue a = x->value; + ExactValue b = y->value; + + GB_ASSERT(are_types_identical(x->type, y->type)); + + TokenKind op = Token_Lt; + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: error(ie->op, "Invalid range operator"); break; + } + bool ok = compare_exact_values(op, a, b); + if (!ok) { + // TODO(bill): Better error message + error(ie->op, "Invalid interval range"); + return false; + } + + ExactValue inline_for_depth = exact_value_sub(b, a); + if (ie->op.kind == Token_Ellipsis) { + inline_for_depth = exact_value_increment_one(inline_for_depth); + } + + if (inline_for_depth_) *inline_for_depth_ = inline_for_depth; + } else { + error(ie->op, "Interval expressions must be constant"); + return false; + } + + add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value); + add_type_and_value(&c->checker->info, ie->right, y->mode, y->type, y->value); + + return true; +} + + ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Stmt; @@ -6697,35 +6789,26 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(bl, BasicLit, node); - // NOTE(bill, 2018-06-17): Placing this in the parser is slower than - // placing it here for some reason. So don't move it to the parsing - // stage if you _think_ it will be faster, only do it if you _know_ it - // will be faster. Type *t = t_invalid; - switch (bl->token.kind) { - case Token_Integer: t = t_untyped_integer; break; - case Token_Float: t = t_untyped_float; break; - case Token_String: t = t_untyped_string; break; - case Token_Rune: t = t_untyped_rune; break; - case Token_Imag: { - String s = bl->token.string; - Rune r = s[s.len-1]; - // NOTE(bill, 2019-08-25): Allow for quaternions by having j and k imaginary numbers - switch (r) { - case 'i': t = t_untyped_complex; break; - case 'j': t = t_untyped_quaternion; break; - case 'k': t = t_untyped_quaternion; break; + switch (bl->value.kind) { + case ExactValue_String: t = t_untyped_string; break; + case ExactValue_Float: t = t_untyped_float; break; + case ExactValue_Complex: t = t_untyped_complex; break; + case ExactValue_Quaternion: t = t_untyped_quaternion; break; + case ExactValue_Integer: + t = t_untyped_integer; + if (bl->token.kind == Token_Rune) { + t = t_untyped_rune; } - break; - } default: - GB_PANIC("Unknown literal"); + GB_PANIC("Unhandled value type for basic literal"); break; } + o->mode = Addressing_Constant; o->type = t; - o->value = exact_value_from_basic_literal(bl->token); + o->value = bl->value; case_end; case_ast_node(bd, BasicDirective, node); @@ -7090,9 +7173,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_simd_vector(t)) { error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); } else { - Map seen = {}; - map_init(&seen, heap_allocator()); - defer (map_destroy(&seen)); + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -7102,36 +7184,89 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } ast_node(fv, FieldValue, elem); - Operand op_index = {}; - check_expr(c, &op_index, fv->field); + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; - if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { - error(elem, "Expected a constant integer as an array field"); - continue; - } + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { + error(x.expr, "Expected a constant integer as an array field"); + continue; + } - i64 index = exact_value_to_i64(op_index.value); + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } - if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); - continue; - } + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + if (op.kind == Token_RangeHalf) { + hi -= 1; + } + i64 max_index = hi; - if (map_get(&seen, hash_integer(u64(index))) != nullptr) { - error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); - continue; - } - map_set(&seen, hash_integer(u64(index)), true); + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); + continue; + } - if (max < index) { - max = index; - } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); + continue; + } + + if (max < max_index) { + max = max_index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } else { + Operand op_index = {}; + check_expr(c, &op_index, fv->field); + + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } - is_constant = is_constant && operand.mode == Addressing_Constant; + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } + + if (max < index) { + max = index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } } cl->max_index = max; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 72913903d..d4398664b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -605,89 +605,15 @@ void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (is_ast_range(expr)) { ast_node(ie, BinaryExpr, expr); - Operand x = {Addressing_Invalid}; - Operand y = {Addressing_Invalid}; + Operand x = {}; + Operand y = {}; - check_expr(ctx, &x, ie->left); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - check_expr(ctx, &y, ie->right); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - convert_to_typed(ctx, &x, y.type); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - convert_to_typed(ctx, &y, x.type); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - convert_to_typed(ctx, &x, default_type(y.type)); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - convert_to_typed(ctx, &y, default_type(x.type)); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - if (!are_types_identical(x.type, y.type)) { - if (x.type != t_invalid && - y.type != t_invalid) { - gbString xt = type_to_string(x.type); - gbString yt = type_to_string(y.type); - gbString expr_str = expr_to_string(x.expr); - error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); - gb_string_free(expr_str); - gb_string_free(yt); - gb_string_free(xt); - } - goto skip_expr; - } - - Type *type = x.type; - if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { - error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); - goto skip_expr; - } - - if (x.mode == Addressing_Constant && - y.mode == Addressing_Constant) { - ExactValue a = x.value; - ExactValue b = y.value; - - GB_ASSERT(are_types_identical(x.type, y.type)); - - TokenKind op = Token_Lt; - switch (ie->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - case Token_RangeHalf: op = Token_Lt; break; - default: error(ie->op, "Invalid range operator"); break; - } - bool ok = compare_exact_values(op, a, b); - if (!ok) { - // TODO(bill): Better error message - error(ie->op, "Invalid interval range"); - goto skip_expr; - } - - inline_for_depth = exact_value_sub(b, a); - if (ie->op.kind == Token_Ellipsis) { - inline_for_depth = exact_value_increment_one(inline_for_depth); - } - - } else { - error(ie->op, "Interval expressions must be constant"); + bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth); + if (!ok) { goto skip_expr; } - add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); - add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); - val0 = type; + val0 = x.type; val1 = t_int; } else { Operand operand = {Addressing_Invalid}; diff --git a/src/common.cpp b/src/common.cpp index db6505a36..b034ad720 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -143,6 +143,9 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) { #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) +#include "range_cache.cpp" + + u64 fnv64a(void const *data, isize len) { u8 const *bytes = cast(u8 const *)data; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 95d04d93b..42b22c8ef 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -293,12 +293,12 @@ ExactValue exact_value_from_basic_literal(Token token) { case 'i': return exact_value_complex(0, imag); case 'j': return exact_value_quaternion(0, 0, imag, 0); case 'k': return exact_value_quaternion(0, 0, 0, imag); + default: GB_PANIC("Invalid imaginary basic literal"); } } case Token_Rune: { Rune r = GB_RUNE_INVALID; gb_utf8_decode(token.string.text, token.string.len, &r); - // gb_printf("%.*s rune: %d\n", LIT(token.string), r); return exact_value_i64(r); } default: diff --git a/src/ir.cpp b/src/ir.cpp index c654e2bf8..5eeba91a2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7865,14 +7865,40 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (ir_is_elem_const(proc->module, fv->value, et)) { continue; } - auto tav = fv->field->tav; - GB_ASSERT(tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(tav.value); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - irCompoundLitElemTempData data = {}; - data.expr = fv->value; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); + irValue *value = ir_build_expr(proc, fv->value); + + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + irCompoundLitElemTempData data = {}; + data.value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } } else { if (ir_is_elem_const(proc->module, elem, et)) { @@ -7897,15 +7923,15 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { defer (proc->return_ptr_hint_value = return_ptr_hint_value); defer (proc->return_ptr_hint_used = return_ptr_hint_used); + irValue *field_expr = temp_data[i].value; Ast *expr = temp_data[i].expr; - if (expr == nullptr) { - continue; - } proc->return_ptr_hint_value = temp_data[i].gep; proc->return_ptr_hint_ast = unparen_expr(expr); - irValue *field_expr = ir_build_expr(proc, expr); + if (field_expr == nullptr) { + field_expr = ir_build_expr(proc, expr); + } Type *t = ir_type(field_expr); GB_ASSERT(t->kind != Type_Tuple); irValue *ev = ir_emit_conv(proc, field_expr, et); @@ -7945,19 +7971,43 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { continue; } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(fv->field->tav.value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); - irValue *field_expr = ir_build_expr(proc, fv->value); - GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } - irValue *ev = ir_emit_conv(proc, field_expr, et); + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(fv->field->tav.value); - irCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); + irValue *field_expr = ir_build_expr(proc, fv->value); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } } else { if (ir_is_elem_const(proc->module, elem, et)) { continue; @@ -8015,14 +8065,36 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { Ast *elem = cl->elems[i]; if (elem->kind == Ast_FieldValue) { ast_node(fv, FieldValue, elem); - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - i64 field_index = exact_value_to_i64(fv->field->tav.value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); - irValue *ev = ir_build_expr(proc, fv->value); - irValue *value = ir_emit_conv(proc, ev, et); - irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); - ir_emit_store(proc, ep, value); + for (i64 k = lo; k < hi; k++) { + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)k); + ir_emit_store(proc, ep, value); + } + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + + i64 field_index = exact_value_to_i64(fv->field->tav.value); + + irValue *ev = ir_build_expr(proc, fv->value); + irValue *value = ir_emit_conv(proc, ev, et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); + ir_emit_store(proc, ep, value); + } } else { irValue *value = ir_emit_conv(proc, ir_build_expr(proc, elem), et); irValue *ep = ir_emit_array_epi(proc, items, cast(i32)i); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index cece0c1db..1d0c8af35 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -887,17 +887,47 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * for (isize j = 0; j < elem_count; j++) { Ast *elem = cl->elems[j]; ast_node(fv, FieldValue, elem); - TypeAndValue index_tav = fv->field->tav; - GB_ASSERT(index_tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(index_tav.value); - if (index == i) { - TypeAndValue tav = fv->value->tav; - if (tav.mode != Addressing_Constant) { + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } + if (lo == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + for (i64 k = lo; k < hi; k++) { + if (k > lo) ir_write_str_lit(f, ", "); + + ir_print_compound_element(f, m, tav.value, elem_type); + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + ir_print_compound_element(f, m, tav.value, elem_type); + found = true; break; } - ir_print_compound_element(f, m, tav.value, elem_type); - found = true; - break; } } diff --git a/src/parser.cpp b/src/parser.cpp index 7f866922a..10aa10119 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -588,6 +588,7 @@ Ast *ast_undef(AstFile *f, Token token) { Ast *ast_basic_lit(AstFile *f, Token basic_lit) { Ast *result = alloc_ast_node(f, Ast_BasicLit); result->BasicLit.token = basic_lit; + result->BasicLit.value = exact_value_from_basic_literal(basic_lit); return result; } @@ -1509,8 +1510,11 @@ Ast *parse_value(AstFile *f) { if (f->curr_token.kind == Token_OpenBrace) { return parse_literal_value(f, nullptr); } - - Ast *value = parse_expr(f, false); + Ast *value; + bool prev_allow_range = f->allow_range; + f->allow_range = true; + value = parse_expr(f, false); + f->allow_range = prev_allow_range; return value; } @@ -1735,7 +1739,8 @@ Ast *parse_operand(AstFile *f, bool lhs) { operand = ast_bad_expr(f, token, f->curr_token); } operand->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ else if (name.string == "file") { return ast_basic_directive(f, token, name.string); + } */ else if (name.string == "file") { + return ast_basic_directive(f, token, name.string); } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string); diff --git a/src/parser.hpp b/src/parser.hpp index 9c3f733e5..83df7a9d6 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -222,6 +222,7 @@ enum StmtAllowFlag { AST_KIND(Undef, "undef", Token) \ AST_KIND(BasicLit, "basic literal", struct { \ Token token; \ + ExactValue value; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ Token token; \ -- cgit v1.2.3 From ee8d3e03f89b1f8065fc9563d84830482bc3f387 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 31 Oct 2019 18:25:39 +0000 Subject: Delay determination of procedure abi types until as late as possible to prevent type undetermination in self-referential data types #454 --- src/check_expr.cpp | 4 ++-- src/check_type.cpp | 20 ++++++++++++-------- src/ir.cpp | 3 +++ src/ir_print.cpp | 5 +++++ src/types.cpp | 9 +++++++-- 5 files changed, 29 insertions(+), 12 deletions(-) (limited to 'src/ir_print.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b5d24e008..51497af7e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -89,7 +89,7 @@ Type * check_init_variable (CheckerContext *c, Entity *e, Operand * Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type); -void set_procedure_abi_types(CheckerContext *c, Type *type); +void set_procedure_abi_types(gbAllocator a, Type *type); void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); Entity *entity_from_expr(Ast *expr) { @@ -963,7 +963,7 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, } if (modify_type) { - set_procedure_abi_types(c, source); + set_procedure_abi_types(c->allocator, source); } return true; diff --git a/src/check_type.cpp b/src/check_type.cpp index de33cc1a5..45c59f2bc 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1273,7 +1273,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) { if (show_error) { - set_procedure_abi_types(ctx, poly_type); + set_procedure_abi_types(ctx->allocator, poly_type); } return poly_type; } @@ -2362,18 +2362,22 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type return false; } -void set_procedure_abi_types(CheckerContext *c, Type *type) { +void set_procedure_abi_types(gbAllocator allocator, Type *type) { type = base_type(type); if (type->kind != Type_Proc) { return; } - type->Proc.abi_compat_params = array_make(c->allocator, cast(isize)type->Proc.param_count); + if (type->Proc.abi_types_set) { + return; + } + + type->Proc.abi_compat_params = array_make(allocator, cast(isize)type->Proc.param_count); for (i32 i = 0; i < type->Proc.param_count; i++) { Entity *e = type->Proc.params->Tuple.variables[i]; if (e->kind == Entity_Variable) { Type *original_type = e->type; - Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type, type->Proc.calling_convention); + Type *new_type = type_to_abi_compat_param_type(allocator, original_type, type->Proc.calling_convention); type->Proc.abi_compat_params[i] = new_type; switch (type->Proc.calling_convention) { case ProcCC_Odin: @@ -2387,8 +2391,10 @@ void set_procedure_abi_types(CheckerContext *c, Type *type) { } // NOTE(bill): The types are the same - type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(c->allocator, type->Proc.results, type->Proc.calling_convention); - type->Proc.return_by_pointer = abi_compat_return_by_pointer(c->allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); + type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(allocator, type->Proc.results, type->Proc.calling_convention); + type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); + + type->Proc.abi_types_set = true; } // NOTE(bill): 'operands' is for generating non generic procedure type @@ -2486,8 +2492,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, } type->Proc.is_polymorphic = is_polymorphic; - set_procedure_abi_types(c, type); - return success; } diff --git a/src/ir.cpp b/src/ir.cpp index ae60be7e3..d4aecebf0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3038,6 +3038,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array const &ar context_ptr = ir_find_or_generate_context_ptr(p); } + set_procedure_abi_types(heap_allocator(), pt); bool is_c_vararg = pt->Proc.c_vararg; isize param_count = pt->Proc.param_count; @@ -10019,6 +10020,8 @@ void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) { void ir_build_proc(irValue *value, irProcedure *parent) { irProcedure *proc = &value->Proc; + set_procedure_abi_types(heap_allocator(), proc->type); + proc->parent = parent; if (proc->body != nullptr) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 1d0c8af35..d47dfc898 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -323,12 +323,15 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) { void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) { + set_procedure_abi_types(heap_allocator(), t); + i64 word_bits = 8*build_context.word_size; t = base_type(t); GB_ASSERT(is_type_proc(t)); isize param_count = t->Proc.param_count; isize result_count = t->Proc.result_count; + ir_print_proc_results(f, m, t); ir_write_string(f, str_lit(" (")); if (t->Proc.return_by_pointer) { @@ -2125,6 +2128,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { + set_procedure_abi_types(heap_allocator(), proc->type); + if (proc->body == nullptr) { ir_write_str_lit(f, "declare "); // if (proc->tags & ProcTag_dll_import) { diff --git a/src/types.cpp b/src/types.cpp index 8ad352f62..bef69ee30 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -205,6 +205,7 @@ struct TypeUnion { Type * abi_compat_result_type; \ i32 variadic_index; \ bool variadic; \ + bool abi_types_set; \ bool require_results; \ bool c_vararg; \ bool is_polymorphic; \ @@ -2360,7 +2361,9 @@ i64 type_size_of(Type *t) { return 0; } // NOTE(bill): Always calculate the size when it is a Type_Basic - if (t->kind != Type_Basic && t->cached_size >= 0) { + if (t->kind == Type_Named && t->cached_size >= 0) { + + } else if (t->kind != Type_Basic && t->cached_size >= 0) { return t->cached_size; } TypePath path = {0}; @@ -2375,7 +2378,9 @@ i64 type_align_of(Type *t) { return 1; } // NOTE(bill): Always calculate the size when it is a Type_Basic - if (t->kind != Type_Basic && t->cached_align > 0) { + if (t->kind == Type_Named && t->cached_align >= 0) { + + } if (t->kind != Type_Basic && t->cached_align > 0) { return t->cached_align; } -- cgit v1.2.3