From 9f51bfcc10f8c2e36abf1dd2536d363f23c64d89 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 19 Sep 2024 01:03:06 +0200 Subject: [PATCH] Support casting bitstructs to bool. --- releasenotes.md | 1 + src/compiler/enums.h | 1 + src/compiler/expr.c | 1 + src/compiler/llvm_codegen_expr.c | 36 +++++++++++ src/compiler/sema_casts.c | 44 ++++++++++++- test/test_suite/bitstruct/bitstruct_bool.c3t | 67 ++++++++++++++++++++ 6 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 test/test_suite/bitstruct/bitstruct_bool.c3t diff --git a/releasenotes.md b/releasenotes.md index 138a604e6..7680a3718 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -11,6 +11,7 @@ - Introduce the `.paramsof` property. - Support environment variable 'C3C_LIB' to find the standard library. - Support environment variable 'C3C_CC' to find the default C compiler. +- Support casting bitstructs to bool. ### Fixes - Issue where a lambda wasn't correctly registered as external. #1408 diff --git a/src/compiler/enums.h b/src/compiler/enums.h index d9ad7056c..4d1f7e1af 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -536,6 +536,7 @@ typedef enum CAST_BOOLINT, CAST_BOOLVECINT, CAST_BSINTARR, + CAST_BSBOOL, CAST_INTARRBS, CAST_EREU, CAST_ERINT, diff --git a/src/compiler/expr.c b/src/compiler/expr.c index fe65067e7..1f2634583 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -373,6 +373,7 @@ static inline bool expr_cast_is_runtime_const(Expr *expr) case CAST_IDINT: case CAST_INTARRBS: case CAST_BSINTARR: + case CAST_BSBOOL: case CAST_SLARR: return exprid_is_runtime_const(expr->cast_expr.expr); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 13ce4235d..51410d248 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1402,6 +1402,31 @@ void llvm_emit_ignored_expr(GenContext *c, Expr *expr) } +static LLVMValueRef llvm_emit_char_array_zero(GenContext *c, BEValue *value, bool find_zero) +{ + llvm_value_addr(c, value); + unsigned len = type_size(value->type); + assert(len > 0); + LLVMValueRef total = NULL; + for (int i = 0; i < len; i++) + { + LLVMValueRef ref = llvm_emit_const_ptradd_inbounds_raw(c, value->value, i); + LLVMValueRef val = llvm_zext_trunc(c, llvm_load(c, c->byte_type, ref, 1, ""), llvm_get_type(c, type_cint)); + total = total ? LLVMBuildAdd(c->builder, total, val, "") : val; + } + return LLVMBuildICmp(c->builder, find_zero ? LLVMIntEQ : LLVMIntNE, total, llvm_get_zero(c, type_cint), ""); +} +static void llvm_emit_bitstruct_to_bool(GenContext *c, BEValue *value, Type *to_type, Type *from_type) +{ + Type *base_type = type_flatten(from_type->decl->bitstruct.base_type->type); + if (base_type->type_kind != TYPE_ARRAY) + { + llvm_emit_int_comp_zero(c, value, value, BINARYOP_NE); + return; + } + llvm_value_set(value, llvm_emit_char_array_zero(c, value, false), to_type); +} + void llvm_emit_cast(GenContext *c, CastKind cast_kind, Expr *expr, BEValue *value, Type *to_type, Type *from_type) { Type *to_type_original = to_type; @@ -1410,6 +1435,9 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, Expr *expr, BEValue *valu switch (cast_kind) { + case CAST_BSBOOL: + llvm_emit_bitstruct_to_bool(c, value, to_type, from_type); + return; case CAST_SLARR: llvm_emit_slice_to_vec_array_cast(c, value, to_type, from_type); return; @@ -2694,6 +2722,14 @@ static void llvm_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr) llvm_value_rvalue(c, value); llvm_value = LLVMBuildIsNull(c->builder, value->value, "not"); break; + case TYPE_ARRAY: + // Handle the bitstruct to bool case. + if (type->array.base == type_char) + { + llvm_value = llvm_emit_char_array_zero(c, value, true); + break; + } + FALLTHROUGH; default: DEBUG_LOG("Unexpectedly tried to not %s", type_quoted_error_string(inner->type)); UNREACHABLE diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 39ae4a46c..a2f66d911 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -358,6 +358,8 @@ CastKind cast_to_bool_kind(Type *type) case TYPE_ANY: case TYPE_INTERFACE: return CAST_ANYBOOL; + case TYPE_BITSTRUCT: + return CAST_BSBOOL; case TYPE_INFERRED_ARRAY: case TYPE_INFERRED_VECTOR: // These should never be here, type should already be known. @@ -371,7 +373,6 @@ CastKind cast_to_bool_kind(Type *type) case TYPE_TYPEID: case TYPE_TYPEINFO: case TYPE_VECTOR: - case TYPE_BITSTRUCT: case TYPE_UNTYPED_LIST: case TYPE_FLEXIBLE_ARRAY: case TYPE_ENUM: @@ -1648,6 +1649,42 @@ static void cast_bitstruct_to_int_arr(SemaContext *context, Expr *expr, Type *ty insert_runtime_cast(expr, CAST_BSINTARR, type); } +static void cast_bitstruct_to_bool(SemaContext *context, Expr *expr, Type *type) +{ + if (expr_is_const(expr)) + { + if (!expr_is_const_initializer(expr) || expr->const_expr.initializer->kind == CONST_INIT_ZERO) + { + expr_rewrite_const_bool(expr, type, false); + return; + } + assert(expr->const_expr.initializer->kind == CONST_INIT_STRUCT); + unsigned elements = vec_size(type_flatten(expr->type)->decl->strukt.members); + for (unsigned i = 0; i < elements; i++) + { + ConstInitializer *in = expr->const_expr.initializer->init_struct[i]; + if (in->kind == CONST_INIT_ZERO) continue; + Expr *e = in->init_value; + if (expr_is_const_bool(e)) + { + if (!e->const_expr.b) continue; + expr_rewrite_const_bool(expr, type, true); + return; + } + if (expr_is_const_int(e)) + { + if (int_is_zero(e->const_expr.ixx)) continue; + expr_rewrite_const_bool(expr, type, true); + return; + } + UNREACHABLE + } + expr_rewrite_const_bool(expr, type, false); + return; + } + insert_runtime_cast(expr, CAST_BSBOOL, type); +} + static void cast_int_arr_to_bitstruct(SemaContext *context, Expr *expr, Type *type) { if (expr->expr_kind == EXPR_CAST && expr->cast_expr.kind == CAST_BSINTARR) @@ -2095,6 +2132,7 @@ static void cast_typeid_to_bool(SemaContext *context, Expr *expr, Type *to_type) #define XX2XX &cast_retype #define BS2IA &cast_bitstruct_to_int_arr +#define BS2BO &cast_bitstruct_to_bool #define IA2BS &cast_int_arr_to_bitstruct #define EX2VC &cast_expand_to_vec #define BO2IN &cast_bool_to_int @@ -2186,7 +2224,7 @@ CastRule cast_rules[CONV_LAST + 1][CONV_LAST + 1] = { {REXPL, _NO__, REXPL, RPTIN, _NO__, RPTPT, _NO__, REXVC, _NO__, RXXDI, _NO__, _NO__, _NO__, ROKOK, RPTIF, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, RPTPT, RPTFE, _NO__}, // PTR {REXPL, _NO__, REXPL, _NO__, _NO__, RSLPT, RSLSL, RSLVA, _NO__, RXXDI, RSLVA, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, ROKOK, RSLPT, RSLFE, _NO__}, // SLICE {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVCVC, _NO__, RXXDI, RVCAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVAFE, _NO__}, // VECTOR - {REXPL, _NO__, _NO__, RBSIN, _NO__, _NO__, _NO__, _NO__, _NO__, RXXDI, RBSAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // BITSTRUCT + {REXPL, _NO__, REXPL, RBSIN, _NO__, _NO__, _NO__, _NO__, _NO__, RXXDI, RBSAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__}, // BITSTRUCT {REXPL, _NO__, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIDI, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, RDIXX, _NO__}, // DISTINCT {REXPL, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RARVC, RARBS, RXXDI, RARAR, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, _NO__, RVAFE, _NO__}, // ARRAY {REXPL, _NO__, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTDI, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, RSTST, _NO__, _NO__}, // STRUCT @@ -2214,7 +2252,7 @@ CastFunction cast_function[CONV_LAST + 1][CONV_LAST + 1] = { {XX2VO, 0, PT2BO, PT2IN, 0, PT2PT, 0, EX2VC, 0, 0, 0, 0, 0, PT2AY, PT2AY, 0, 0, 0, 0, 0, PT2PT, PT2PT, PT2FE, 0 }, // PTR {XX2VO, 0, SL2BO, 0, 0, SL2PT, SL2SL, SL2VA, 0, 0, SL2VA, 0, 0, 0, 0, 0, 0, 0, 0, 0, SL2PT, SL2PT, SL2FE, 0 }, // SLICE {XX2VO, 0, 0, 0, 0, 0, 0, VC2VC, 0, 0, VC2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE, 0 }, // VECTOR - {XX2VO, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BITSTRUCT + {XX2VO, 0, BS2BO, BS2IA, 0, 0, 0, 0, 0, 0, BS2IA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // BITSTRUCT { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // DISTINCT {XX2VO, 0, 0, 0, 0, 0, 0, AR2VC, IA2BS, 0, AR2AR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, VA2FE, 0 }, // ARRAY {XX2VO, 0, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, 0, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN, ST2LN,ST2LN, ST2LN, ST2LN, ST2LN, 0, 0 }, // STRUCT diff --git a/test/test_suite/bitstruct/bitstruct_bool.c3t b/test/test_suite/bitstruct/bitstruct_bool.c3t new file mode 100644 index 000000000..0ef74af7a --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_bool.c3t @@ -0,0 +1,67 @@ +// #target: macos-x64 +module test; +bitstruct Foo : int +{ + bool enable_help; + bool enable_version; +} + +bitstruct Foo2 : char[2] +{ + bool enable_help; + bool enable_version; +} + +fn void main() +{ + Foo a = { .enable_help }; + Foo b = { .enable_version }; + Foo $a = { .enable_help }; + Foo $b = { .enable_version }; + $assert(!($a & $b)); + if (a & b) return; + Foo2 a2 = { .enable_help }; + Foo2 b2 = { .enable_version, .enable_help }; + if (!(a2 & b2)) return; +} + +/* #expect: test.ll + + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %a2 = alloca [2 x i8], align 1 + %b2 = alloca [2 x i8], align 1 + %0 = alloca i16, align 1 + store i32 1, ptr %a, align 4 + store i32 2, ptr %b, align 4 + %1 = load i32, ptr %a, align 4 + %2 = load i32, ptr %b, align 4 + %and = and i32 %1, %2 + %neq = icmp ne i32 %and, 0 + br i1 %neq, label %if.then, label %if.exit + +if.then: ; preds = %entry + ret void + +if.exit: ; preds = %entry + store [2 x i8] c"\01\00", ptr %a2, align 1 + store [2 x i8] c"\03\00", ptr %b2, align 1 + %3 = load i16, ptr %a2, align 1 + %4 = load i16, ptr %b2, align 1 + %and1 = and i16 %3, %4 + store i16 %and1, ptr %0, align 1 + %5 = load i8, ptr %0, align 1 + %zext = zext i8 %5 to i32 + %ptradd = getelementptr inbounds i8, ptr %0, i64 1 + %6 = load i8, ptr %ptradd, align 1 + %zext2 = zext i8 %6 to i32 + %7 = add i32 %zext, %zext2 + %8 = icmp eq i32 %7, 0 + br i1 %8, label %if.then3, label %if.exit4 + +if.then3: ; preds = %if.exit + ret void + +if.exit4: ; preds = %if.exit + ret void +} \ No newline at end of file