From 05f0059b1b888fe49036389b74d63ade5c40a607 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 17 Jan 2022 16:06:17 +0100 Subject: [PATCH] Fix float conversion. Strings as array initializers work much better. --- src/compiler/compiler_internal.h | 14 +++ src/compiler/llvm_codegen_expr.c | 65 +++++++++++--- src/compiler/sema_casts.c | 8 +- src/compiler/sema_expr.c | 2 +- src/version.h | 2 +- test/test_suite/abi/riscv64-lp64-abi.c3t | 4 +- test/test_suite/clang/2002-03.c3t | 93 ++++++++++++++++++++ test/test_suite/functions/assorted_tests.c3t | 4 +- test/test_suite/strings/string_to_array.c3t | 4 +- 9 files changed, 175 insertions(+), 21 deletions(-) create mode 100644 test/test_suite/clang/2002-03.c3t diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2a3a75af8..48ba3eea4 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2370,6 +2370,20 @@ static inline Type *type_flatten(Type *type) } } +static inline bool type_is_char_array(Type *type) +{ + type = type_flatten_distinct(type); + if (type->type_kind != TYPE_ARRAY) return false; + switch (type->array.base->type_kind) + { + case TYPE_I8: + case TYPE_U8: + return true; + default: + return false; + } +} + static Type *type_vector_type(Type *type) { Type *flatten = type_flatten(type); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 531aa5761..5bb0f16f4 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -3876,17 +3876,60 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) return; case CONST_STRING: { - LLVMValueRef global_name = LLVMAddGlobal(c->module, LLVMArrayType(llvm_get_type(c, type_char), expr->const_expr.string.len + 1), ".str"); - llvm_set_private_linkage(global_name); - LLVMSetUnnamedAddress(global_name, LLVMGlobalUnnamedAddr); - LLVMSetGlobalConstant(global_name, 1); - LLVMSetInitializer(global_name, LLVMConstStringInContext(c->context, - expr->const_expr.string.chars, - expr->const_expr.string.len, - 0)); - llvm_set_alignment(global_name, 1); - global_name = LLVMConstBitCast(global_name, llvm_get_ptr_type(c, type_get_array(type_char, expr->const_expr.string.len))); - llvm_value_set(be_value, global_name, type); + Type *str_type = type_lowering(expr->type); + bool is_array = type_is_char_array(str_type); + if (c->builder || !is_array) + { + ArraySize strlen = expr->const_expr.string.len; + ArraySize size = expr->const_expr.string.len + 1; + if (type_is_char_array(expr->type) && type->array.len > size) size = type->array.len; + LLVMValueRef global_name = LLVMAddGlobal(c->module, LLVMArrayType(llvm_get_type(c, type_char), size), ".str"); + llvm_set_private_linkage(global_name); + LLVMSetUnnamedAddress(global_name, LLVMGlobalUnnamedAddr); + LLVMSetGlobalConstant(global_name, 1); + LLVMValueRef string = LLVMConstStringInContext(c->context, + expr->const_expr.string.chars, + expr->const_expr.string.len, + 0); + if (size > strlen + 1) + { + LLVMValueRef trailing_zeros = LLVMConstNull(LLVMArrayType(c->byte_type, size - strlen - 1)); + LLVMValueRef values[2] = { string, trailing_zeros }; + string = LLVMConstStructInContext(c->context, values, 2, true); + } + LLVMSetInitializer(global_name, string); + llvm_set_alignment(global_name, 1); + if (is_array) + { + global_name = LLVMConstBitCast(global_name, llvm_get_ptr_type(c, type)); + llvm_value_set_address(be_value, global_name, type, 1); + } + else + { + global_name = LLVMConstBitCast(global_name, llvm_get_type(c, type)); + llvm_value_set(be_value, global_name, type); + } + return; + } + ArraySize array_len = type->array.len; + ArraySize size = expr->const_expr.string.len + 1; + bool zero_terminate = array_len == size; + LLVMValueRef string; + if (array_len <= size) + { + string = LLVMConstStringInContext(c->context, + expr->const_expr.string.chars, + array_len, array_len < size); + } + else + { + char *buffer = ccalloc(1, array_len); + memcpy(buffer, expr->const_expr.string.chars, expr->const_expr.string.len); + string = LLVMConstStringInContext(c->context, + buffer, + array_len, true); + } + llvm_value_set(be_value, string, type); return; } case CONST_TYPEID: diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index f7cb1a092..d06146f3e 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -199,7 +199,7 @@ static bool float_to_float(Expr* expr, Type *canonical, Type *type) */ bool float_to_integer(Expr *expr, Type *canonical, Type *type) { - bool is_signed = type_is_unsigned(canonical); + bool is_signed = type_is_signed(canonical); if (insert_runtime_cast_unless_const(expr, is_signed ? CAST_FPSI : CAST_FPUI, type)) return true; assert(type_is_integer(canonical)); @@ -1026,7 +1026,11 @@ static inline bool cast_maybe_string_lit_to_char_array(Expr *expr, Type *expr_ca Type *pointer = expr_canonical->pointer; if (pointer->type_kind != TYPE_ARRAY) return false; if (pointer->array.base != type_char) return false; - expr_insert_deref(expr); + if (to_canonical->type_kind == TYPE_INFERRED_ARRAY) + { + to_canonical = type_get_array(to_canonical->array.base, pointer->array.len); + } + expr->type = to_canonical; return true; } bool cast_implicit(Expr *expr, Type *to_type) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 82a9ff5d8..118ffa930 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -426,8 +426,8 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_UNARY: switch (expr->unary_expr.operator) { - case UNARYOP_ERROR: case UNARYOP_DEREF: + case UNARYOP_ERROR: return false; case UNARYOP_ADDR: return expr_unary_addr_is_constant_eval(expr, eval_kind); diff --git a/src/version.h b/src/version.h index 11b9f7836..ae9815f40 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.14" \ No newline at end of file +#define COMPILER_VERSION "PRE.15" \ No newline at end of file diff --git a/test/test_suite/abi/riscv64-lp64-abi.c3t b/test/test_suite/abi/riscv64-lp64-abi.c3t index 8b976715d..aabe81de4 100644 --- a/test/test_suite/abi/riscv64-lp64-abi.c3t +++ b/test/test_suite/abi/riscv64-lp64-abi.c3t @@ -38,8 +38,8 @@ entry: %9 = bitcast <32 x i8>* %4 to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 32 %8, i8* align 32 %9, i32 32, i1 false) %10 = getelementptr inbounds %Large, %Large* %literal, i32 0, i32 0 - %fpui = fptoui double %1 to i64 - store i64 %fpui, i64* %10, align 8 + %fpsi = fptosi double %1 to i64 + store i64 %fpsi, i64* %10, align 8 %11 = getelementptr inbounds %Large, %Large* %literal, i32 0, i32 1 %uisiext = zext i8 %5 to i64 store i64 %uisiext, i64* %11, align 8 diff --git a/test/test_suite/clang/2002-03.c3t b/test/test_suite/clang/2002-03.c3t new file mode 100644 index 000000000..ecf8fbc31 --- /dev/null +++ b/test/test_suite/clang/2002-03.c3t @@ -0,0 +1,93 @@ +// #target: x64-darwin +module test; +fn int strcmp(char *s1, char *s2); + +fn int test(char *x) { + /* LLVM-GCC used to emit: + %.LC0 = internal global [3 x sbyte] c"\1F\FFFFFF8B\00" + */ + return strcmp(x, "\x1f\x8B"); +} + +int[10] a = { [0] = 0, [1] = 2 }; + +char[10] str = "x"; + +void*[5] arr = { [0] = null, [1] = null }; + +float[12] f = { [0] = 1.23f, [1] = 34.7f }; + +struct Test { int x; double y; } + +Test[10] array = { [0] = { 2, 12.0 }, [1] = { 3, 24.0 } }; + +int[4][4] b = { [0] = { 1, 2, 3, 4}, [1] = { [0] = 5, [1] = 6, [2] = 7 }, [2] = { [0] = 8, [1] = 9 } }; + +struct Connection { + long to; + char[10] type; + long length; +} + +Connection[3] link += { {1, "link1", 10}, + {2, "link2", 20}, + {3, "link3", 30} }; + + +fn int trys(char *s, int x) +{ + int asa; + double val; + int lLS; + if (x) { + asa = lLS + asa; + } else { + } + return asa+(int)(val); +} + +/* #expect: test.ll + +@test.a = local_unnamed_addr global { i32, i32, [8 x i32] } { i32 0, i32 2, [8 x i32] zeroinitializer }, align 16 +@test.str = local_unnamed_addr global [10 x i8] c"x\00\00\00\00\00\00\00\00\00", align 1 +@test.arr = local_unnamed_addr global { i8*, i8*, [3 x i8*] } zeroinitializer, align 16 +@test.f = local_unnamed_addr global { float, float, [10 x float] } { float 0x3FF3AE1480000000, float 0x40415999A0000000, [10 x float] zeroinitializer }, align 16 +@test.array = local_unnamed_addr global { %Test, %Test, [8 x %Test] } { %Test { i32 2, double 1.200000e+01 }, %Test { i32 3, double 2.400000e+01 }, [8 x %Test] zeroinitializer }, align 16 +@test.b = local_unnamed_addr global { [4 x i32], { i32, i32, i32, i32 }, { i32, i32, [2 x i32] }, [4 x i32] } { [4 x i32] [i32 1, i32 2, i32 3, i32 4], { i32, i32, i32, i32 } { i32 5, i32 6, i32 7, i32 0 }, { i32, i32, [2 x i32] } { i32 8, i32 9, [2 x i32] zeroinitializer }, [4 x i32] zeroinitializer }, align 16 +@test.link = local_unnamed_addr global [3 x %Connection] [%Connection { i64 1, [10 x i8] c"link1\00\00\00\00\00", i64 10 }, %Connection { i64 2, [10 x i8] c"link2\00\00\00\00\00", i64 20 }, %Connection { i64 3, [10 x i8] c"link3\00\00\00\00\00", i64 30 }], align 16 +@.str = private unnamed_addr constant [4 x i8] c"\1F\C2\8B\00", align 1 + +declare i32 @test.strcmp(i8*, i8*) #0 + +define i32 @test.test(i8* %0) #0 { +entry: + %1 = call i32 @test.strcmp(i8* %0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0)) + ret i32 %1 +} + +define i32 @test.trys(i8* %0, i32 %1) #0 { +entry: + %asa = alloca i32, align 4 + %val = alloca double, align 8 + %lLS = alloca i32, align 4 + store i32 0, i32* %asa, align 4 + store double 0.000000e+00, double* %val, align 8 + store i32 0, i32* %lLS, align 4 + %intbool = icmp ne i32 %1, 0 + br i1 %intbool, label %if.then, label %if.exit + +if.then: ; preds = %entry + %2 = load i32, i32* %lLS, align 4 + %3 = load i32, i32* %asa, align 4 + %add = add i32 %2, %3 + store i32 %add, i32* %asa, align 4 + br label %if.exit + +if.exit: ; preds = %if.then, %entry + %4 = load i32, i32* %asa, align 4 + %5 = load double, double* %val, align 8 + %fpsi = fptosi double %5 to i32 + %add1 = add i32 %4, %fpsi + ret i32 %add1 +} \ No newline at end of file diff --git a/test/test_suite/functions/assorted_tests.c3t b/test/test_suite/functions/assorted_tests.c3t index 5351c2b3e..ae15f105f 100644 --- a/test/test_suite/functions/assorted_tests.c3t +++ b/test/test_suite/functions/assorted_tests.c3t @@ -90,8 +90,8 @@ if.then: ; preds = %entry if.exit: ; preds = %if.then, %entry %4 = load i32, i32* %asa, align 4 %5 = load double, double* %val, align 8 - %fpui = fptoui double %5 to i32 - %add1 = add i32 %4, %fpui + %fpsi = fptosi double %5 to i32 + %add1 = add i32 %4, %fpsi ret i32 %add1 } diff --git a/test/test_suite/strings/string_to_array.c3t b/test/test_suite/strings/string_to_array.c3t index 9a72352c7..305dce451 100644 --- a/test/test_suite/strings/string_to_array.c3t +++ b/test/test_suite/strings/string_to_array.c3t @@ -18,8 +18,8 @@ entry: %x = alloca [2 x i8], align 1 %y = alloca [3 x i8], align 1 %0 = bitcast [2 x i8]* %x to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %0, i8* align 8 getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i32 2, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %0, i8* align 1 getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i32 2, i1 false) %1 = bitcast [3 x i8]* %y to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %1, i8* align 8 getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 3, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %1, i8* align 1 getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 3, i1 false) ret i32 0 }