Skip to content

Commit

Permalink
Support casting bitstructs to bool.
Browse files Browse the repository at this point in the history
  • Loading branch information
lerno committed Sep 18, 2024
1 parent 9426e81 commit 9f51bfc
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 3 deletions.
1 change: 1 addition & 0 deletions releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/compiler/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ typedef enum
CAST_BOOLINT,
CAST_BOOLVECINT,
CAST_BSINTARR,
CAST_BSBOOL,
CAST_INTARRBS,
CAST_EREU,
CAST_ERINT,
Expand Down
1 change: 1 addition & 0 deletions src/compiler/expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
36 changes: 36 additions & 0 deletions src/compiler/llvm_codegen_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down
44 changes: 41 additions & 3 deletions src/compiler/sema_casts.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
67 changes: 67 additions & 0 deletions test/test_suite/bitstruct/bitstruct_bool.c3t
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 9f51bfc

Please sign in to comment.