From 4f09b0c351a0aef73ab74e8a4515a4545e8bae59 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 1 Nov 2021 23:48:34 +0100 Subject: [PATCH] Bitstruct implementation. --- src/compiler/compiler_internal.h | 10 +- src/compiler/copying.c | 6 +- src/compiler/enums.h | 4 + src/compiler/llvm_codegen.c | 13 +- src/compiler/llvm_codegen_expr.c | 618 ++++++++- src/compiler/llvm_codegen_internal.h | 8 +- src/compiler/llvm_codegen_stmt.c | 3 + src/compiler/parse_expr.c | 1 + src/compiler/parse_global.c | 28 +- src/compiler/sema_casts.c | 4 + src/compiler/sema_decls.c | 130 +- src/compiler/sema_expr.c | 45 +- src/compiler/symtab.c | 2 + src/compiler/types.c | 1 + src/utils/lib.h | 6 + .../bitstruct/address_of_bitstruct.c3 | 15 + .../bitstruct/array_with_boolean.c3t | 71 + test/test_suite/bitstruct/bitfield_access.c3t | 180 +++ .../bitstruct/bitstruct_access_signed.c3t | 92 ++ .../test_suite/bitstruct/bitstruct_arrays.c3t | 1146 +++++++++++++++++ .../bitstruct/bitstruct_arrays_be.c3t | 146 +++ .../test_suite/bitstruct/bitstruct_general.c3 | 35 +- .../bitstruct/bitstruct_intcontainer.c3t | 106 ++ .../test_suite/bitstruct/bitstruct_overlap.c3 | 47 + .../bitstruct/bitstruct_single_error.c3 | 19 + .../bitstruct/missing_bitstruct_type.c3 | 3 + wrapper/src/wrapper.cpp | 7 + 27 files changed, 2686 insertions(+), 60 deletions(-) create mode 100644 test/test_suite/bitstruct/address_of_bitstruct.c3 create mode 100644 test/test_suite/bitstruct/array_with_boolean.c3t create mode 100644 test/test_suite/bitstruct/bitfield_access.c3t create mode 100644 test/test_suite/bitstruct/bitstruct_access_signed.c3t create mode 100644 test/test_suite/bitstruct/bitstruct_arrays.c3t create mode 100644 test/test_suite/bitstruct/bitstruct_arrays_be.c3t create mode 100644 test/test_suite/bitstruct/bitstruct_intcontainer.c3t create mode 100644 test/test_suite/bitstruct/bitstruct_overlap.c3 create mode 100644 test/test_suite/bitstruct/bitstruct_single_error.c3 create mode 100644 test/test_suite/bitstruct/missing_bitstruct_type.c3 diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 2edf8d901..9af627dbf 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -350,6 +350,10 @@ typedef struct { TypeInfo *base_type; Decl **members; + bool msb0 : 1; + bool big_endian : 1; + bool little_endian : 1; + bool overlap : 1; } BitStructDecl; typedef struct VarDecl_ @@ -370,12 +374,14 @@ typedef struct VarDecl_ void *backend_debug_ref; unsigned scope_depth; Expr *start; + int start_bit; }; union { void *failable_ref; struct ABIArgInfo_ *abi_info; Expr *end; + int end_bit; }; } VarDecl; @@ -1613,6 +1619,7 @@ uint64_t int_to_u64(Int op); int64_t int_to_i64(Int op); bool int_is_zero(Int op); bool int_fits(Int op1, TypeKind kind); +Int int_rightmost_bits(Int op, unsigned to_bits, TypeKind result_type); Int int_conv(Int op, TypeKind to_type); Int int_div(Int op1, Int op2); Int int_mul(Int op1, Int op2); @@ -2381,7 +2388,7 @@ static inline bool decl_is_callable_type(Decl *decl) static inline bool decl_is_user_defined_type(Decl *decl) { DeclKind kind = decl->decl_kind; - return (kind == DECL_UNION) | (kind == DECL_STRUCT) + return (kind == DECL_UNION) | (kind == DECL_STRUCT) | (kind == DECL_BITSTRUCT) | (kind == DECL_ENUM) | (kind == DECL_DISTINCT); } @@ -2389,6 +2396,7 @@ static inline DeclKind decl_from_token(TokenType type) { if (type == TOKEN_STRUCT) return DECL_STRUCT; if (type == TOKEN_UNION) return DECL_UNION; + if (type == TOKEN_BITSTRUCT) return DECL_BITSTRUCT; UNREACHABLE } diff --git a/src/compiler/copying.c b/src/compiler/copying.c index e2e33194a..1b9fed76a 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -158,6 +158,7 @@ Expr *copy_expr(Expr *source_expr) case EXPR_CONST: return expr; case EXPR_BINARY: + case EXPR_BITASSIGN: MACRO_COPY_EXPR(expr->binary_expr.left); MACRO_COPY_EXPR(expr->binary_expr.right); return expr; @@ -184,6 +185,7 @@ Expr *copy_expr(Expr *source_expr) MACRO_COPY_EXPR(expr->subscript_expr.expr); MACRO_COPY_EXPR(expr->subscript_expr.index); return expr; + case EXPR_BITACCESS: case EXPR_ACCESS: MACRO_COPY_EXPR(expr->access_expr.parent); return expr; @@ -463,6 +465,8 @@ Decl *copy_decl(Decl *decl) MACRO_COPY_DECL_LIST(copy->strukt.members); MACRO_COPY_DECL_LIST(copy->methods); break; + case DECL_BITSTRUCT: + UNREACHABLE case DECL_ENUM: case DECL_ERRTYPE: copy_decl_type(copy); @@ -493,8 +497,6 @@ Decl *copy_decl(Decl *decl) MACRO_COPY_EXPR(copy->var.init_expr); } break; - case DECL_BITSTRUCT: - TODO case DECL_LABEL: TODO break; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index ff1834ab3..b83165823 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -171,6 +171,8 @@ typedef enum { EXPR_POISONED, EXPR_ACCESS, + EXPR_BITACCESS, + EXPR_BITASSIGN, EXPR_BINARY, EXPR_MACRO_BODY_EXPANSION, EXPR_CALL, @@ -629,6 +631,8 @@ typedef enum ATTRIBUTE_INLINE, ATTRIBUTE_NOINLINE, ATTRIBUTE_OPAQUE, + ATTRIBUTE_BIGENDIAN, + ATTRIBUTE_LITTLEENDIAN, ATTRIBUTE_NORETURN, ATTRIBUTE_SECTION, ATTRIBUTE_EXTNAME, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 92d463279..0421c90f0 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -217,6 +217,10 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_ } case CONST_INIT_STRUCT: { + if (const_init->type->type_kind == TYPE_BITSTRUCT) + { + return llvm_emit_const_bitstruct(c, const_init); + } Decl *decl = const_init->type->decl; Decl **members = decl->strukt.members; MemberIndex count = vec_size(members); @@ -530,6 +534,7 @@ static bool intrinsics_setup = false; unsigned intrinsic_id_trap; unsigned intrinsic_id_assume; +unsigned intrinsic_id_bswap; unsigned intrinsic_id_ssub_overflow; unsigned intrinsic_id_ssub_sat; unsigned intrinsic_id_usub_overflow; @@ -612,6 +617,8 @@ void llvm_codegen_setup() intrinsic_id_trap = lookup_intrinsic("llvm.trap"); intrinsic_id_assume = lookup_intrinsic("llvm.assume"); + intrinsic_id_bswap = lookup_intrinsic("llvm.bswap"); + intrinsic_id_lifetime_start = lookup_intrinsic("llvm.lifetime.start"); intrinsic_id_lifetime_end = lookup_intrinsic("llvm.lifetime.end"); @@ -850,7 +857,7 @@ LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value) value->alignment ? value->alignment : type_abi_alignment(value->type), ""); case BE_BOOLEAN: - if (!c->builder) + if (LLVMIsConstant(value->value)) { return LLVMConstZExt(value->value, c->byte_type); } @@ -1141,6 +1148,10 @@ void llvm_attribute_add_string(GenContext *context, LLVMValueRef value_to_add_at LLVMAddAttributeAtIndex(value_to_add_attribute_to, index, llvm_attr); } +unsigned llvm_bitsize(GenContext *c, LLVMTypeRef type) +{ + return LLVMSizeOfTypeInBits(c->target_data, type); +} unsigned llvm_abi_size(GenContext *c, LLVMTypeRef type) { return LLVMABISizeOfType(c->target_data, type); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 487224c05..1c8bef3e7 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5,6 +5,8 @@ #include "llvm_codegen_internal.h" #include +void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op); +static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr); static void gencontext_emit_unary_expr(GenContext *context, BEValue *value, Expr *expr); static inline void llvm_emit_post_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod); static inline void llvm_emit_pre_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod); @@ -19,6 +21,80 @@ LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value) return LLVMBuildICmp(c->builder, LLVMIntEQ, value->value, llvm_get_zero(c, type_anyerr), "not_err"); } +static inline LLVMValueRef llvm_emit_extract_value(GenContext *c, LLVMValueRef agg, unsigned index) +{ + if (LLVMIsConstant(agg)) + { + return LLVMConstExtractValue(agg, &index, 1); + } + else + { + return LLVMBuildExtractValue(c->builder, agg, index, ""); + } +} + +static inline LLVMValueRef llvm_emit_insert_value(GenContext *c, LLVMValueRef agg, LLVMValueRef new_value, unsigned index) +{ + if (LLVMIsConstant(agg) && LLVMIsConstant(new_value)) + { + return LLVMConstInsertValue(agg, new_value, &index, 1); + } + else + { + return LLVMBuildInsertValue(c->builder, agg, new_value, index, ""); + } +} + +static inline LLVMValueRef llvm_zext_trunc(GenContext *c, LLVMValueRef data, LLVMTypeRef type) +{ + LLVMTypeRef current_type = LLVMTypeOf(data); + if (current_type == type) return data; + assert(LLVMGetTypeKind(type) == LLVMIntegerTypeKind); + assert(LLVMGetTypeKind(current_type) == LLVMIntegerTypeKind); + if (llvm_bitsize(c, current_type) < llvm_bitsize(c, type)) + { + if (LLVMIsConstant(data)) + { + return LLVMConstZExt(data, type); + } + return LLVMBuildZExt(c->builder, data, type, ""); + } + assert(llvm_bitsize(c, current_type) > llvm_bitsize(c, type)); + if (LLVMIsConstant(data)) + { + return LLVMConstTrunc(data, type); + } + return LLVMBuildTrunc(c->builder, data, type, ""); +} + +static inline LLVMValueRef llvm_emit_lshr(GenContext *c, LLVMValueRef data, int shift) +{ + assert(shift >= 0); + if (shift == 0) return data; + LLVMTypeRef type = LLVMTypeOf(data); + int bit_width = llvm_bitsize(c, type); + if (shift >= bit_width) return LLVMConstNull(type); + if (LLVMIsAConstant(data)) + { + return LLVMConstLShr(data, LLVMConstInt(type, shift, false)); + } + return LLVMBuildLShr(c->builder, data, LLVMConstInt(type, shift, false), ""); +} + +static inline LLVMValueRef llvm_emit_shl(GenContext *c, LLVMValueRef data, int shift) +{ + assert(shift >= 0); + if (shift == 0) return data; + LLVMTypeRef type = LLVMTypeOf(data); + int bit_width = llvm_bitsize(c, type); + if (shift >= bit_width) return LLVMConstNull(type); + if (LLVMIsAConstant(data)) + { + return LLVMConstShl(data, LLVMConstInt(type, shift, false)); + } + return LLVMBuildShl(c->builder, data, LLVMConstInt(type, shift, false), ""); +} + void llvm_convert_vector_comparison(GenContext *c, BEValue *be_value, LLVMValueRef val, Type *vector_type) { Type *result_type = type_get_vector_bool(vector_type); @@ -76,6 +152,33 @@ LLVMValueRef llvm_emit_aggregate_value(GenContext *c, Type *type, ...) return result; } +LLVMValueRef llvm_const_low_bitmask(LLVMTypeRef type, int type_bits, int low_bits) +{ + if (low_bits < 1) return LLVMConstNull(type); + if (type_bits <= low_bits) return LLVMConstAllOnes(type); + return LLVMConstLShr(LLVMConstAllOnes(type), LLVMConstInt(type, type_bits - low_bits, 0)); +} + +LLVMValueRef llvm_const_high_bitmask(LLVMTypeRef type, int type_bits, int high_bits) +{ + if (high_bits < 1) return LLVMConstNull(type); + if (type_bits <= high_bits) return LLVMConstAllOnes(type); + return LLVMConstNot(LLVMConstLShr(LLVMConstAllOnes(type), LLVMConstInt(type, high_bits, 0))); +} + +LLVMValueRef llvm_mask_low_bits(GenContext *c, LLVMValueRef value, int type_bits, int low_bits) +{ + LLVMTypeRef type = LLVMTypeOf(value); + if (low_bits < 1) return LLVMConstNull(type); + if (type_bits <= low_bits) return value; + LLVMValueRef mask = LLVMConstLShr(LLVMConstAllOnes(type), LLVMConstInt(type, type_bits - low_bits, 0)); + if (LLVMIsConstant(value)) + { + return LLVMConstAnd(mask, value); + } + return LLVMBuildAnd(c->builder, mask, value, ""); +} + LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size) { assert(size > 0); @@ -496,6 +599,358 @@ static void gencontext_emit_member_addr(GenContext *c, BEValue *value, Decl *par } while (found != member); } +static LLVMValueRef llvm_emit_bswap(GenContext *c, LLVMValueRef value) +{ + if (LLVMIsConstant(value)) + { + return LLVMConstBswap(value); + } + LLVMTypeRef type = LLVMTypeOf(value); + return llvm_emit_call_intrinsic(c, intrinsic_id_bswap, &type, 1, &value, 1); +} + + + + +/** + * The super simple case is extracting a bool from a char array: + * 1. Grab the byte + * 2. Rightshift + * 3. Truncate to 1 bit + */ +static inline void llvm_extract_bool_bit_from_array(GenContext *c, BEValue *be_value, Decl *member) +{ + AlignSize alignment; + LLVMValueRef array_ptr = be_value->value; + LLVMTypeRef array_type = llvm_get_type(c, type_char); + int start_bit = member->var.start_bit; + // Grab the byte + LLVMValueRef byte_ptr = llvm_emit_array_gep_raw(c, array_ptr, llvm_get_type(c, be_value->type), start_bit / 8, be_value->alignment, &alignment); + LLVMValueRef element = llvm_emit_load_aligned(c, array_type, byte_ptr, alignment, ""); + // Shift the bit to the zero position. + element = llvm_emit_lshr(c, element, start_bit % 8); + // Truncate to i1. + element = LLVMBuildTrunc(c->builder, element, c->bool_type, ""); + // Done! + llvm_value_set_bool(be_value, element); +} + +static inline LLVMValueRef llvm_bswap_non_integral(GenContext *c, LLVMValueRef value, int bitsize) +{ + if (bitsize <= 8) return value; + LLVMValueRef shifted = llvm_emit_shl(c, value, llvm_bitsize(c, LLVMTypeOf(value)) - bitsize); + return llvm_emit_bswap(c, shifted); +} + +static inline void llvm_extract_bitvalue_from_array(GenContext *c, BEValue *be_value, Decl *member, Decl *parent_decl) +{ + llvm_value_addr(c, be_value); + if (type_lowering(member->type) == type_bool) + { + llvm_extract_bool_bit_from_array(c, be_value, member); + return; + } + bool big_endian = platform_target.big_endian; + if (parent_decl->bitstruct.big_endian) big_endian = true; + if (parent_decl->bitstruct.little_endian) big_endian = false; + int start = member->var.start_bit; + int end = member->var.end_bit; + LLVMValueRef array_ptr = be_value->value; + LLVMTypeRef array_type = llvm_get_type(c, type_char); + LLVMValueRef result = NULL; + int start_byte = start / 8; + int end_byte = end / 8; + Type *member_type = type_lowering(member->type); + LLVMTypeRef llvm_member_type = llvm_get_type(c, member_type); + int bitsize = type_size(member_type) * 8; + LLVMValueRef res = NULL; + unsigned offset = start % 8; + for (int i = start_byte; i <= end_byte; i++) + { + AlignSize alignment; + LLVMValueRef byte_ptr = llvm_emit_array_gep_raw(c, array_ptr, llvm_get_type(c, be_value->type), i, be_value->alignment, &alignment); + LLVMValueRef element = llvm_emit_load_aligned(c, array_type, byte_ptr, alignment, ""); + element = llvm_zext_trunc(c, element, llvm_member_type); + int current_offset = 8 * (i - start_byte) - offset; + if (current_offset < 0) + { + element = llvm_emit_lshr(c, element, -current_offset); + } + else if (current_offset > 0) + { + element = llvm_emit_shl(c, element, current_offset); + } + if (res == NULL) + { + res = element; + continue; + } + res = LLVMBuildOr(c->builder, element, res, ""); + } + if (big_endian) + { + res = llvm_bswap_non_integral(c, res, end - start + 1); + } + if (type_is_signed(member_type)) + { + int top_bits_to_clear = bitsize - end + start - 1; + if (top_bits_to_clear) + { + LLVMValueRef shift = LLVMConstInt(llvm_member_type, top_bits_to_clear, false); + res = LLVMBuildShl(c->builder, res, shift, ""); + res = LLVMBuildAShr(c->builder, res, shift, ""); + } + } + else + { + res = llvm_mask_low_bits(c, res, bitsize, end - start + 1); + } + llvm_value_set(be_value, res, member_type); +} + + +static inline void llvm_extract_bitvalue(GenContext *c, BEValue *be_value, Expr *parent, Decl *member) +{ + Decl *parent_decl = type_flatten(parent->type)->decl; + if (be_value->type->type_kind == TYPE_ARRAY) + { + llvm_extract_bitvalue_from_array(c, be_value, member, parent_decl); + return; + } + bool bswap = false; + if (parent_decl->bitstruct.big_endian && !platform_target.big_endian) bswap = true; + if (parent_decl->bitstruct.little_endian && platform_target.big_endian) bswap = true; + LLVMValueRef value = llvm_value_rvalue_store(c, be_value); + if (bswap) value = llvm_emit_bswap(c, value); + LLVMTypeRef container_type = LLVMTypeOf(value); + ByteSize container_size = type_size(be_value->type); + ByteSize container_bit_size = container_size * 8; + int start = member->var.start_bit; + int end = member->var.end_bit; + Type *member_type = type_lowering(member->type); + ByteSize member_type_size = type_size(member_type); + if (type_is_signed(member_type)) + { + // Shift all the way left, so top bit is to the top. + int left_shift = container_bit_size - end - 1; + if (left_shift) + { + value = LLVMBuildShl(c->builder, value, LLVMConstInt(container_type, left_shift, 0), ""); + } + int right_shift = left_shift + start; + if (right_shift) + { + value = LLVMBuildAShr(c->builder, value, LLVMConstInt(container_type, right_shift, 0), ""); + } + if (member_type_size < container_bit_size) + { + value = LLVMBuildTrunc(c->builder, value, llvm_get_type(c, member_type), ""); + } + else if (member_type_size > container_bit_size) + { + value = LLVMBuildSExt(c->builder, value, llvm_get_type(c, member_type), ""); + } + } + else + { + // Shift away bottom: + if (start) + { + value = LLVMBuildLShr(c->builder, value, LLVMConstInt(container_type, start, 0), ""); + } + int bits_needed = end - start + 1; + if (bits_needed < container_bit_size && bits_needed > 1) + { + LLVMValueRef mask = LLVMConstLShr(LLVMConstAllOnes(container_type), LLVMConstInt(container_type, container_bit_size - bits_needed - 1, 0)); + value = LLVMBuildAnd(c->builder, value, mask, ""); + } + if (member_type_size < container_bit_size) + { + value = LLVMBuildTrunc(c->builder, value, llvm_get_type(c, member_type), ""); + } + else if (member_type_size > container_bit_size) + { + value = LLVMBuildZExt(c->builder, value, llvm_get_type(c, member_type), ""); + } + } + llvm_value_set(be_value, value, member_type); +} + +static inline void llvm_emit_bitassign_array(GenContext *c, BEValue *result, BEValue parent, Decl *parent_decl, Decl *member) +{ + // We could possibly do this on a value as well. However, this + // is unlikely to be the common case, so use addr. + llvm_value_addr(c, &parent); + LLVMValueRef array_ptr = parent.value; + assert(parent.type->type_kind == TYPE_ARRAY); + LLVMTypeRef array_type = llvm_get_type(c, parent.type); + + int start_bit = member->var.start_bit; + int end_bit = member->var.end_bit; + + Type *member_type = type_flatten(member->type); + LLVMValueRef value = llvm_value_rvalue_store(c, result); + if (member_type == type_bool) + { + assert(start_bit == end_bit); + value = llvm_emit_shl(c, value, start_bit % 8); + AlignSize alignment; + LLVMValueRef byte_ptr = llvm_emit_array_gep_raw(c, array_ptr, array_type, start_bit / 8, parent.alignment, &alignment); + LLVMValueRef current = llvm_emit_load_aligned(c, c->byte_type, byte_ptr, alignment, ""); + LLVMValueRef bit = llvm_emit_shl(c, LLVMConstInt(c->byte_type, 1, 0), start_bit % 8); + current = LLVMBuildAnd(c->builder, current, LLVMConstNot(bit), ""); + current = LLVMBuildOr(c->builder, current, value, ""); + llvm_store_aligned(c, byte_ptr, current, alignment); + return; + } + + bool big_endian = platform_target.big_endian; + if (parent_decl->bitstruct.big_endian) big_endian = true; + if (parent_decl->bitstruct.little_endian) big_endian = false; + + unsigned bit_size = end_bit - start_bit + 1; + if (big_endian) + { + value = llvm_bswap_non_integral(c, value, bit_size); + } + assert(bit_size > 0 && bit_size <= 128); + int start_byte = start_bit / 8; + int end_byte = end_bit / 8; + int start_mod = start_bit % 8; + int end_mod = end_bit % 8; + ByteSize member_type_bitsize = type_size(member_type) * 8; + for (int i = start_byte; i <= end_byte; i++) + { + AlignSize alignment; + LLVMValueRef byte_ptr = llvm_emit_array_gep_raw(c, array_ptr, array_type, i, parent.alignment, &alignment); + if (i == start_byte && start_mod != 0) + { + int skipped_bits = start_mod; + // Shift the lower bits into the top of the byte. + LLVMValueRef res = llvm_emit_shl(c, value, skipped_bits); + // Then truncate. + if (member_type_bitsize > 8) + { + res = llvm_zext_trunc(c, res, c->byte_type); + } + // Create a mask for the lower bits. + LLVMValueRef mask = llvm_const_low_bitmask(c->byte_type, 8, skipped_bits); + + // We might need to mask the top bits + if (i == end_byte && end_mod != 7) + { + res = LLVMBuildAnd(c->builder, res, llvm_const_low_bitmask(c->byte_type, 8, end_mod + 1), ""); + mask = LLVMConstOr(mask, llvm_const_high_bitmask(c->byte_type, 8, 7 - end_bit)); + } + // Load the current value. + LLVMValueRef current = llvm_emit_load_aligned(c, c->byte_type, byte_ptr, alignment, ""); + // Empty the top bits. + current = LLVMBuildAnd(c->builder, current, mask, ""); + // Use *or* with the top bits from "res": + current = LLVMBuildOr(c->builder, current, res, ""); + // And store it back. + llvm_store_aligned(c, byte_ptr, current, alignment); + // We now shift the value by the number of bits we used. + value = llvm_emit_lshr(c, value, 8 - skipped_bits); + // ... and we're done with the first byte. + continue; + } + if (i == end_byte && end_mod != 7) + { + // What remains is end_mod + 1 bits to copy. + value = llvm_zext_trunc(c, value, c->byte_type); + // Create a mask for the lower bits. + LLVMValueRef mask = llvm_const_low_bitmask(c->byte_type, 8, end_mod + 1); + value = LLVMBuildAnd(c->builder, value, mask, ""); + // Load the current value. + LLVMValueRef current = llvm_emit_load_aligned(c, c->byte_type, byte_ptr, alignment, ""); + // Clear the lower bits. + current = LLVMBuildAnd(c->builder, current, LLVMConstNot(mask), ""); + // Use *or* with the bottom bits from "value": + current = LLVMBuildOr(c->builder, current, value, ""); + // And store it back. + llvm_store_aligned(c, byte_ptr, current, alignment); + continue; + } + // All others are simple: truncate & store + llvm_store_aligned(c, byte_ptr, llvm_zext_trunc(c, value, c->byte_type), alignment); + // Then shift + value = llvm_emit_lshr(c, value, 8); + } +} +static inline void llvm_emit_bitassign_expr(GenContext *c, BEValue *be_value, Expr *expr) +{ + Expr *lhs = expr->binary_expr.left; + Expr *parent_expr = lhs->access_expr.parent; + + // Grab the parent + BEValue parent; + llvm_emit_expr(c, &parent, parent_expr); + + Decl *member = lhs->access_expr.ref; + + // If we have assign + op, load the current value, perform the operation. + if (expr->binary_expr.operator != BINARYOP_ASSIGN) + { + // Grab the current value. + BEValue value = parent; + llvm_extract_bitvalue(c, &value, parent_expr, member); + // Perform the operation and place it in be_value + gencontext_emit_binary(c, be_value, expr, &value, binaryop_assign_base_op(expr->binary_expr.operator)); + } + else + { + // Otherwise just resolve the rhs and place it in be_value + llvm_emit_expr(c, be_value, expr->binary_expr.right); + } + + if (type_lowering(parent_expr->type)->type_kind == TYPE_ARRAY) + { + llvm_emit_bitassign_array(c, be_value, parent, type_flatten_distinct(parent_expr->type)->decl, member); + return; + } + + // To start the assign, pull out the current value. + LLVMValueRef current_value = llvm_value_rvalue_store(c, &parent); + + // Get the type. + LLVMTypeRef struct_type = LLVMTypeOf(current_value); + + // We now need to create a mask, a very naive algorithm: + LLVMValueRef mask = LLVMConstAllOnes(struct_type); + int bits = type_size(parent.type) * 8; + int start_bit = member->var.start_bit; + int end_bit = member->var.end_bit; + // Let's say we want to create 00111000 => start: 3 end: 5 + int left_shift = bits - end_bit - 1; + mask = llvm_emit_shl(c, mask, left_shift); + // => shift 2: 11111100 + mask = llvm_emit_lshr(c, mask, left_shift + start_bit); + // => shift 5: 00000111 + mask = llvm_emit_shl(c, mask, start_bit); + // => shift 3: 00111000 + + // Now we might need to truncate or widen the value to insert: + LLVMValueRef value = llvm_value_rvalue_store(c, be_value); + int value_bitsize = type_size(be_value->type) * 8; + value = llvm_zext_trunc(c, value, struct_type); + // Shift to the correct location. + value = llvm_emit_shl(c, value, start_bit); + // And combine using ((current_value & ~mask) | (value & mask)) + value = LLVMBuildAnd(c->builder, value, mask, ""); + current_value = LLVMBuildAnd(c->builder, current_value, LLVMConstNot(mask), ""); + current_value = LLVMBuildOr(c->builder, current_value, value, ""); + llvm_store_bevalue_raw(c, &parent, current_value); +} +static inline void llvm_emit_bitaccess(GenContext *c, BEValue *be_value, Expr *expr) +{ + Expr *parent = expr->access_expr.parent; + llvm_emit_expr(c, be_value, parent); + + assert(be_value && be_value->type); + + llvm_extract_bitvalue(c, be_value, parent, expr->access_expr.ref); +} static inline void gencontext_emit_access_addr(GenContext *context, BEValue *be_value, Expr *expr) { @@ -579,14 +1034,7 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_ to_type = type_lowering(to_type); from_type = type_lowering(from_type); llvm_value_rvalue(c, value); - if (type_convert_will_trunc(to_type, from_type)) - { - value->value = LLVMBuildTrunc(c->builder, value->value, llvm_get_type(c, to_type), "interrtrunc"); - } - else - { - value->value = LLVMBuildZExt(c->builder, value->value, llvm_get_type(c, to_type), "erruiext"); - } + value->value = llvm_zext_trunc(c, value->value, llvm_get_type(c, to_type)); break; case CAST_ERROR: UNREACHABLE @@ -1133,6 +1581,140 @@ static inline void llvm_emit_initialize_reference_designated(GenContext *c, BEVa llvm_emit_initialize_designated(c, ref, 0, designator->designator_expr.path, last_element, designator->designator_expr.value, NULL); } } +LLVMValueRef llvm_emit_const_bitstruct_array(GenContext *c, ConstInitializer *initializer) +{ + Decl *decl = initializer->type->decl; + Type *base_type = decl->bitstruct.base_type->type; + bool big_endian = platform_target.big_endian; + if (decl->bitstruct.big_endian) big_endian = true; + if (decl->bitstruct.little_endian) big_endian = false; + LLVMValueRef data = LLVMConstNull(llvm_get_type(c, base_type)); + Decl **members = decl->strukt.members; + MemberIndex count = vec_size(members); + for (MemberIndex i = 0; i < count; i++) + { + Decl *member = members[i]; + unsigned start_bit = member->var.start_bit; + unsigned end_bit = member->var.end_bit; + Type *member_type = type_flatten(member->type); + assert(initializer->init_struct[i]->kind == CONST_INIT_VALUE); + Expr *expr = initializer->init_struct[i]->init_value; + + // Special case for bool + if (member_type == type_bool) + { + assert(expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_BOOL); + assert(start_bit == end_bit); + + // Completely skip zero. + if (!expr->const_expr.b) continue; + + LLVMValueRef bit = llvm_emit_shl(c, LLVMConstInt(c->byte_type, 1, 0), start_bit % 8); + int byte = start_bit / 8; + LLVMValueRef current_value = llvm_emit_extract_value(c, data, byte); + data = llvm_emit_insert_value(c, data, LLVMConstOr(current_value, bit), byte); + continue; + } + unsigned bit_size = end_bit - start_bit + 1; + assert(bit_size > 0 && bit_size <= 128); + BEValue val; + llvm_emit_const_expr(c, &val, initializer->init_struct[i]->init_value); + assert(val.kind == BE_VALUE); + LLVMValueRef value = val.value; + int start_byte = start_bit / 8; + int end_byte = end_bit / 8; + ByteSize member_type_bitsize = type_size(member_type) * 8; + value = llvm_mask_low_bits(c, value, member_type_bitsize, bit_size); + if (big_endian && bit_size > 8) + { + value = llvm_bswap_non_integral(c, value, bit_size); + } + int bit_offset = start_bit % 8; + for (int j = start_byte; j <= end_byte; j++) + { + LLVMValueRef to_or; + if (j == start_byte) + { + to_or = llvm_emit_shl(c, value, bit_offset); + } + else + { + to_or = llvm_emit_lshr(c, value, j * 8 - start_bit); + } + if (j == end_byte) + { + to_or = llvm_mask_low_bits(c, to_or, member_type_bitsize, end_bit % 8 + 1); + } + if (member_type_bitsize > 8) to_or = LLVMConstTrunc(to_or, c->byte_type); + LLVMValueRef current_value = llvm_emit_extract_value(c, data, j); + data = llvm_emit_insert_value(c, data, LLVMConstOr(to_or, current_value), j); + } + } + return data; +} + +LLVMValueRef llvm_emit_const_bitstruct(GenContext *c, ConstInitializer *initializer) +{ + Decl *decl = initializer->type->decl; + Type *base_type = decl->bitstruct.base_type->type; + if (initializer->kind == CONST_INIT_ZERO) return llvm_get_zero(c, base_type); + bool char_array = base_type->type_kind == TYPE_ARRAY; + if (char_array) + { + return llvm_emit_const_bitstruct_array(c, initializer); + } + LLVMTypeRef llvm_base_type = llvm_get_type(c, base_type); + LLVMValueRef result = LLVMConstNull(llvm_base_type); + Decl **members = decl->strukt.members; + MemberIndex count = vec_size(members); + ByteSize base_type_size = type_size(base_type); + ByteSize base_type_bitsize = base_type_size * 8; + for (MemberIndex i = 0; i < count; i++) + { + Decl *member = members[i]; + ByteSize member_size = type_size(member->type); + unsigned start_bit = member->var.start_bit; + unsigned end_bit = member->var.end_bit; + unsigned bit_size = end_bit - start_bit + 1; + assert(bit_size > 0 && bit_size <= 128); + assert(initializer->init_struct[i]->kind == CONST_INIT_VALUE); + BEValue entry; + llvm_emit_const_expr(c, &entry, initializer->init_struct[i]->init_value); + LLVMValueRef value = llvm_value_rvalue_store(c, &entry); + value = llvm_zext_trunc(c, value, llvm_base_type); + if (bit_size < base_type_bitsize) + { + LLVMValueRef mask = LLVMConstAllOnes(llvm_base_type); + mask = LLVMConstLShr(mask, LLVMConstInt(llvm_base_type, base_type_bitsize - bit_size, 0)); + value = LLVMConstAnd(mask, value); + } + if (start_bit > 0) + { + value = LLVMConstShl(value, LLVMConstInt(llvm_base_type, start_bit, 0)); + } + result = LLVMConstOr(value, result); + } + if (decl->bitstruct.little_endian && platform_target.big_endian) + { + return LLVMConstBswap(result); + } + if (decl->bitstruct.big_endian && !platform_target.big_endian) + { + return LLVMConstBswap(result); + } + return result; +} + +static inline void llvm_emit_const_initialize_bitstruct_ref(GenContext *c, BEValue *ref, ConstInitializer *initializer) +{ + if (initializer->kind == CONST_INIT_ZERO) + { + llvm_emit_memclear(c, ref); + return; + } + assert(initializer->kind == CONST_INIT_STRUCT); + llvm_store_bevalue_raw(c, ref, llvm_emit_const_bitstruct(c, initializer)); +} /** * Initialize a constant aggregate type. @@ -1142,6 +1724,11 @@ static inline void llvm_emit_const_initialize_reference(GenContext *c, BEValue * assert(expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_LIST); ConstInitializer *initializer = expr->const_expr.list; assert(!type_is_vector(initializer->type) && "Vectors should be handled elsewhere."); + if (initializer->type->type_kind == TYPE_BITSTRUCT) + { + llvm_emit_const_initialize_bitstruct_ref(c, ref, initializer); + return; + } if (initializer->kind == CONST_INIT_ZERO) { REMINDER("Optimize this for few elements"); @@ -2805,6 +3392,7 @@ static void llvm_emit_binary_expr(GenContext *c, BEValue *be_value, Expr *expr) } if (binary_op == BINARYOP_ASSIGN) { + Expr *left = expr->binary_expr.left; llvm_emit_expr(c, be_value, expr->binary_expr.left); assert(llvm_value_is_addr(be_value)); LLVMValueRef failable_ref = NULL; @@ -2941,7 +3529,7 @@ static LLVMValueRef llvm_emit_real(LLVMTypeRef type, Float f) static inline void llvm_emit_const_initializer_list_expr(GenContext *c, BEValue *value, Expr *expr) { - if (!c->builder || type_is_vector(expr->type)) + if (!c->builder || type_is_vector(expr->type) || type_flatten_distinct(expr->type)->type_kind == TYPE_BITSTRUCT) { llvm_value_set(value, llvm_emit_const_initializer(c, expr->const_expr.list), expr->type); return; @@ -3166,6 +3754,12 @@ LLVMValueRef llvm_emit_array_gep_raw(GenContext *c, LLVMValueRef ptr, LLVMTypeRe return llvm_emit_array_gep_raw_index(c, ptr, array_type, llvm_const_int(c, type_usize, index), array_alignment, alignment); } +LLVMValueRef llvm_emit_array_load(GenContext *c, LLVMValueRef ptr, LLVMTypeRef array_type, unsigned index, AlignSize array_alignment) +{ + AlignSize alignment; + LLVMValueRef element_ptr = llvm_emit_array_gep_raw_index(c, ptr, array_type, llvm_const_int(c, type_usize, index), array_alignment, &alignment); + return llvm_emit_load_aligned(c, LLVMGetElementType(array_type), element_ptr, alignment, ""); +} LLVMValueRef llvm_emit_pointer_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset) { @@ -4295,6 +4889,9 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_MACRO_BODY_EXPANSION: llvm_emit_macro_body_expansion(c, value, expr); return; + case EXPR_BITASSIGN: + llvm_emit_bitassign_expr(c, value, expr); + return; case EXPR_BINARY: llvm_emit_binary_expr(c, value, expr); return; @@ -4333,6 +4930,9 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_CAST: gencontext_emit_cast_expr(c, value, expr); return; + case EXPR_BITACCESS: + llvm_emit_bitaccess(c, value, expr); + return; } UNREACHABLE } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 1b92c0080..01568ed60 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -115,6 +115,7 @@ extern unsigned intrinsic_id_ushl_sat; extern unsigned intrinsic_id_smul_overflow; extern unsigned intrinsic_id_umul_overflow; extern unsigned intrinsic_id_trap; +extern unsigned intrinsic_id_bswap; extern unsigned intrinsic_id_assume; extern unsigned intrinsic_id_fmuladd; extern unsigned intrinsic_id_rint; @@ -185,6 +186,8 @@ void gencontext_init_file_emit(GenContext *c, Context *ast); void gencontext_end_file_emit(GenContext *c, Context *ast); void gencontext_end_module(GenContext *context); +LLVMValueRef LLVMConstBswap(LLVMValueRef ConstantVal); + // BE value void llvm_value_addr(GenContext *c, BEValue *value); static inline bool llvm_value_is_addr(BEValue *value) { return value->kind == BE_ADDRESS || value->kind == BE_ADDRESS_FAILABLE; } @@ -204,6 +207,7 @@ LLVMValueRef llvm_value_rvalue_store(GenContext *c, BEValue *value); LLVMTypeRef llvm_abi_type(GenContext *c, AbiType *type); unsigned llvm_abi_size(GenContext *c, LLVMTypeRef type); +unsigned llvm_bitsize(GenContext *c, LLVMTypeRef type); AlignSize llvm_abi_alignment(GenContext *c, LLVMTypeRef type); void llvm_attribute_add_range(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index_start, int index_end); void llvm_attribute_add(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, int index); @@ -218,12 +222,13 @@ LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size); LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size); LLVMValueRef llvm_emit_alloca(GenContext *c, LLVMTypeRef type, unsigned alignment, const char *name); LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *name); +void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl); BEValue llvm_emit_assign_expr(GenContext *context, BEValue *ref, Expr *expr, LLVMValueRef failable); static inline LLVMValueRef llvm_emit_bitcast(GenContext *context, LLVMValueRef value, Type *type); void llvm_emit_block(GenContext *c, LLVMBasicBlockRef next_block); void llvm_emit_br(GenContext *c, LLVMBasicBlockRef next_block); void llvm_emit_compound_stmt(GenContext *context, Ast *ast); -void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl); +LLVMValueRef llvm_emit_const_bitstruct(GenContext *c, ConstInitializer *initializer); void llvm_emit_convert_value_from_coerced(GenContext *c, BEValue *result, LLVMTypeRef coerced, LLVMValueRef value, Type *original_type); void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignment, LLVMTypeRef coerced, LLVMValueRef value, LLVMTypeRef target_type); void llvm_emit_function_body(GenContext *context, Decl *decl); @@ -271,6 +276,7 @@ LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLV unsigned struct_alignment, AlignSize *alignment); LLVMValueRef llvm_emit_array_gep_raw(GenContext *c, LLVMValueRef ptr, LLVMTypeRef array_type, unsigned index, AlignSize array_alignment, AlignSize *alignment); LLVMValueRef llvm_emit_array_gep_raw_index(GenContext *c, LLVMValueRef ptr, LLVMTypeRef array_type, LLVMValueRef index, AlignSize array_alignment, AlignSize *alignment); +LLVMValueRef llvm_emit_array_load(GenContext *c, LLVMValueRef ptr, LLVMTypeRef array_type, unsigned index, AlignSize array_alignment); LLVMValueRef llvm_emit_pointer_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset); LLVMValueRef llvm_emit_pointer_inbounds_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 49e5334ff..86b178bf3 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -975,6 +975,8 @@ static bool expr_is_pure(Expr *expr) case EXPR_IDENTIFIER: case EXPR_NOP: return true; + case EXPR_BITASSIGN: + return false; case EXPR_BINARY: if (expr->binary_expr.operator >= BINARYOP_ASSIGN) return false; return expr_is_pure(expr->binary_expr.right) && expr_is_pure(expr->binary_expr.left); @@ -989,6 +991,7 @@ static bool expr_is_pure(Expr *expr) return expr_is_pure(expr->unary_expr.expr); } break; + case EXPR_BITACCESS: case EXPR_ACCESS: return expr_is_pure(expr->access_expr.parent); case EXPR_POISONED: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 82b9ccd99..b220303a8 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1104,6 +1104,7 @@ static Expr *parse_integer(Context *context, Expr *left) return poisoned_expr; } } + if (type_bits && type_bits < 8) type_bits = 8; if (type_bits && !is_power_of_two(type_bits)) type_bits = next_highest_power_of_2(type_bits); } if (!type_bits) diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 71322b838..c2728f4d7 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -3,6 +3,7 @@ static Decl *parse_const_declaration(Context *context, Visibility visibility); static inline Decl *parse_func_definition(Context *context, Visibility visibility, bool is_interface); +static inline bool parse_bitstruct_body(Context *context, Decl *decl); static bool context_next_is_path_prefix_start(Context *context) { @@ -1267,7 +1268,7 @@ bool parse_struct_body(Context *context, Decl *parent) while (!TOKEN_IS(TOKEN_RBRACE)) { TokenType token_type = context->tok.type; - if (token_type == TOKEN_STRUCT || token_type == TOKEN_UNION) + if (token_type == TOKEN_STRUCT || token_type == TOKEN_UNION || token_type == TOKEN_BITSTRUCT) { DeclKind decl_kind = decl_from_token(token_type); Decl *member; @@ -1284,11 +1285,16 @@ bool parse_struct_body(Context *context, Decl *parent) member->span.loc = context->prev_tok; advance_and_verify(context, TOKEN_IDENT); } - if (!parse_attributes(context, &member->attributes)) return false; - if (!parse_struct_body(context, member)) + if (decl_kind == DECL_BITSTRUCT) { - decl_poison(parent); - return false; + TRY_CONSUME_OR(TOKEN_COLON, "':' followed by bitstruct type (e.g. 'int') was expected here.", poisoned_decl); + ASSIGN_TYPE_ELSE(member->bitstruct.base_type, parse_type(context), poisoned_decl); + if (!parse_bitstruct_body(context, member)) return decl_poison(parent); + } + else + { + if (!parse_attributes(context, &member->attributes)) return false; + if (!parse_struct_body(context, member)) return decl_poison(parent); } vec_add(parent->strukt.members, member); index++; @@ -1404,8 +1410,14 @@ static inline bool parse_bitstruct_body(Context *context, Decl *decl) Decl *member_decl = decl_new_var(context->prev_tok, type, VARDECL_MEMBER, VISIBLE_LOCAL); CONSUME_OR(TOKEN_COLON, false); ASSIGN_EXPR_ELSE(member_decl->var.start, parse_constant_expr(context), false); - CONSUME_OR(TOKEN_DOTDOT, false); - ASSIGN_EXPR_ELSE(member_decl->var.end, parse_constant_expr(context), false); + if (try_consume(context, TOKEN_DOTDOT)) + { + ASSIGN_EXPR_ELSE(member_decl->var.end, parse_constant_expr(context), false); + } + else + { + member_decl->var.end = NULL; + } CONSUME_OR(TOKEN_EOS, false); vec_add(decl->bitstruct.members, member_decl); } @@ -1424,7 +1436,7 @@ static inline Decl *parse_bitstruct_declaration(Context *context, Visibility vis if (!consume_type_name(context, "bitstruct")) return poisoned_decl; Decl *decl = decl_new_with_type(name, DECL_BITSTRUCT, visibility); - CONSUME_OR(TOKEN_COLON, poisoned_decl); + TRY_CONSUME_OR(TOKEN_COLON, "':' followed by bitstruct type (e.g. 'int') was expected here.", poisoned_decl); ASSIGN_TYPE_ELSE(decl->bitstruct.base_type, parse_type(context), poisoned_decl); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 926834089..8dbf6d333 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -779,6 +779,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) switch (expr->expr_kind) { case EXPR_BINARY: + case EXPR_BITASSIGN: switch (expr->binary_expr.operator) { case BINARYOP_ERROR: @@ -824,6 +825,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type) case EXPR_MACRO_BODY_EXPANSION: case EXPR_CALL: case EXPR_POISONED: + case EXPR_BITACCESS: case EXPR_ACCESS: case EXPR_CATCH_UNWRAP: case EXPR_COMPOUND_LITERAL: @@ -929,6 +931,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) { switch (expr->expr_kind) { + case EXPR_BITASSIGN: case EXPR_BINARY: switch (expr->binary_expr.operator) { @@ -975,6 +978,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type) case EXPR_MACRO_BODY_EXPANSION: case EXPR_CALL: case EXPR_POISONED: + case EXPR_BITACCESS: case EXPR_ACCESS: case EXPR_CATCH_UNWRAP: case EXPR_COMPOUND_LITERAL: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index bcd3ac09a..07e50857a 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -94,6 +94,7 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl) return true; case DECL_STRUCT: case DECL_UNION: + case DECL_BITSTRUCT: return sema_analyse_decl(context, decl); default: UNREACHABLE @@ -395,9 +396,99 @@ static bool sema_analyse_struct_union(Context *context, Decl *decl) return decl_ok(decl); } +static inline bool sema_analyse_bitstruct_member(Context *context, Decl *decl, unsigned index, bool allow_overlap) +{ + Decl **members = decl->strukt.members; + Decl *member = members[index]; + if (!sema_resolve_type_info(context, member->var.type_info)) return false; + Type *member_type = type_flatten_for_bitstruct(member->var.type_info->type); + member->type = member->var.type_info->type; + if (!type_is_integer(member_type) && member_type != type_bool) + { + SEMA_ERROR(member->var.type_info, "%s is not supported in a bitstruct, only enums, integer and boolean values may be used.", + type_quoted_error_string(member->var.type_info->type)); + return false; + } + int bits = type_size(decl->bitstruct.base_type->type) * 8; + Int max_bits = (Int) { .type = TYPE_I64, .i = { .low = bits } }; + Expr *start = member->var.start; + Expr *end = member->var.end; + if (!sema_analyse_expr(context, start)) return false; + int start_bit; + int end_bit; + if (start->expr_kind != EXPR_CONST || !type_is_integer(start->type) || int_is_neg(start->const_expr.ixx)) + { + SEMA_ERROR(start, "This must be a constant non-negative integer value."); + return false; + } + if (int_comp(start->const_expr.ixx, max_bits, BINARYOP_GE)) + { + SEMA_ERROR(start, "Expected at the most a bit index of %d\n", bits - 1); + return false; + } + end_bit = start_bit = start->const_expr.ixx.i.low; + if (end) + { + if (!sema_analyse_expr(context, start)) return false; + if (end->expr_kind != EXPR_CONST || !type_is_integer(end->type) || int_is_neg(end->const_expr.ixx)) + { + SEMA_ERROR(end, "This must be a constant non-negative integer value."); + return false; + } + if (int_comp(end->const_expr.ixx, max_bits, BINARYOP_GE)) + { + SEMA_ERROR(end, "Expected at the most a bit index of %d.", bits - 1); + return false; + } + end_bit = end->const_expr.ixx.i.low; + } + else + { + if (member_type->type_kind != TYPE_BOOL) + { + SEMA_ERROR(member, "Only booleans may use non-range indices, try using %d..%d instead.", start_bit, start_bit); + return false; + } + } + if (start_bit > end_bit) + { + SEMA_ERROR(start, "The start bit must be smaller than the end bit index."); + return false; + } + int bitsize_type = member_type == type_bool ? 1 : type_size(member_type) * 8; + int bits_available = end_bit + 1 - start_bit; + if (bitsize_type < bits_available) + { + SEMA_ERROR(member, "The bit width of %s (%d) is less than the assigned bits (%d), try reducing the range.", + type_quoted_error_string(member->type), bitsize_type, bits_available); + return false; + } + member->var.start_bit = start_bit; + member->var.end_bit = end_bit; + for (unsigned i = 0; i < index; i++) + { + Decl *other_member = members[i]; + if (member->name == other_member->name) + { + SEMA_ERROR(member, "Duplicate members with the name '%s'.", member->name); + SEMA_PREV(other_member, "The other member was declared here."); + return false; + } + if (allow_overlap) continue; + // Check for overlap. + if ((start_bit >= other_member->var.start_bit || end_bit >= other_member->var.start_bit) + && start_bit <= other_member->var.end_bit) + { + SEMA_ERROR(member, "Overlapping members, please use '@overlap' if this is intended."); + SEMA_PREV(other_member, "The other member was declared here."); + return false; + } + } + return true; +} + static bool sema_analyse_bitstruct(Context *context, Decl *decl) { - int overlap = -1; VECEACH(decl->attributes, i) { Attr *attr = decl->attributes[i]; @@ -406,17 +497,30 @@ static bool sema_analyse_bitstruct(Context *context, Decl *decl) if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); bool had = false; -#define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break +#define SET_ATTR(_X) had = decl->bitstruct._X; decl->bitstruct._X = true; break switch (attribute) { case ATTRIBUTE_OVERLAP: - had = (overlap != -1); - overlap = 1; + SET_ATTR(overlap); break; case ATTRIBUTE_OPAQUE: had = decl->is_opaque; decl->is_opaque = true; break; + case ATTRIBUTE_BIGENDIAN: + if (decl->bitstruct.little_endian) + { + SEMA_TOKID_ERROR(attr->name, "Attribute cannot be combined with @littleendian"); + return decl_poison(decl); + } + SET_ATTR(big_endian); + case ATTRIBUTE_LITTLEENDIAN: + if (decl->bitstruct.big_endian) + { + SEMA_TOKID_ERROR(attr->name, "Attribute cannot be combined with @bigendian"); + return decl_poison(decl); + } + SET_ATTR(little_endian); default: UNREACHABLE } @@ -443,24 +547,14 @@ static bool sema_analyse_bitstruct(Context *context, Decl *decl) SCOPE_START VECEACH(members, i) { - Decl *member = members[i]; - if (!sema_resolve_type_info(context, member->var.type_info)) + if (!sema_analyse_bitstruct_member(context, decl, i, decl->bitstruct.overlap)) { success = false; - continue; - } - Type *member_type = type_flatten_for_bitstruct(member->var.type_info->type); - if (!type_is_integer(member_type) && member_type != type_bool) - { - SEMA_ERROR(member->var.type_info, "%s is not supported in a bitstruct, only enums, integer and boolean values may be used.", - type_quoted_error_string(member->var.type_info->type)); - success = false; - continue; + break; } } SCOPE_END; if (!success) return decl_poison(decl); - TODO return decl_ok(decl); } @@ -867,7 +961,9 @@ AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDoma [ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_LOCAL | ATTR_GLOBAL | ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER, [ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL, [ATTRIBUTE_NOINLINE] = ATTR_FUNC | ATTR_CALL, - [ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION, + [ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION | ATTR_BITSTRUCT, + [ATTRIBUTE_BIGENDIAN] = ATTR_BITSTRUCT, + [ATTRIBUTE_LITTLEENDIAN] = ATTR_BITSTRUCT, [ATTRIBUTE_USED] = ~ATTR_CALL, [ATTRIBUTE_UNUSED] = ~ATTR_CALL, [ATTRIBUTE_NAKED] = ATTR_FUNC, diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 3ad2ee32f..e96a98bbe 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -240,9 +240,12 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) RETRY: switch (expr->expr_kind) { + case EXPR_BITACCESS: case EXPR_ACCESS: expr = expr->access_expr.parent; goto RETRY; + case EXPR_BITASSIGN: + return false; case EXPR_BINARY: return expr_binary_is_constant_eval(expr, eval_kind); case EXPR_CAST: @@ -445,6 +448,7 @@ bool expr_is_ltype(Expr *expr) } case EXPR_UNARY: return expr->unary_expr.operator == UNARYOP_DEREF; + case EXPR_BITACCESS: case EXPR_ACCESS: return expr_is_ltype(expr->access_expr.parent); case EXPR_GROUP: @@ -712,6 +716,7 @@ static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.identifier, parent_decl); case DECL_UNION: case DECL_STRUCT: + case DECL_BITSTRUCT: return false; default: UNREACHABLE @@ -2303,7 +2308,7 @@ static void add_members_to_context(Context *context, Decl *decl) if (!type_is_user_defined(type)) break; decl = type->decl; } - if (decl_is_struct_type(decl)) + if (decl_is_struct_type(decl) || decl->decl_kind == DECL_BITSTRUCT) { Decl **members = decl->strukt.members; VECEACH(members, i) @@ -2529,6 +2534,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T case DECL_UNION: case DECL_STRUCT: case DECL_DISTINCT: + case DECL_BITSTRUCT: break; default: UNREACHABLE @@ -2559,7 +2565,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T return false; } - if (member->decl_kind == DECL_UNION || member->decl_kind == DECL_STRUCT) + if (member->decl_kind == DECL_UNION || member->decl_kind == DECL_STRUCT || member->decl_kind == DECL_BITSTRUCT) { expr->expr_kind = EXPR_TYPEINFO; expr->type_expr->type = member->type; @@ -2607,7 +2613,6 @@ static inline bool sema_expr_analyse_access(Context *context, Expr *expr) return sema_expr_analyse_type_access(context, expr, parent->type_expr, was_group, is_macro, identifier_token); } - // 6. Copy failability bool failable = IS_FAILABLE(parent); @@ -2704,6 +2709,12 @@ CHECK_DEEPER: return false; } + // Transform bitstruct access to expr_bitaccess. + if (decl->decl_kind == DECL_BITSTRUCT) + { + expr->expr_kind = EXPR_BITACCESS; + } + // 13. Copy properties. expr->access_expr.parent = current_parent; expr->type = type_get_opt_fail(member->type, failable); @@ -3463,6 +3474,7 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to case TYPE_STRUCT: case TYPE_UNION: case TYPE_ARRAY: + case TYPE_BITSTRUCT: case TYPE_INFERRED_ARRAY: case TYPE_VECTOR: return sema_expr_analyse_initializer(context, to, assigned, expr); @@ -3698,6 +3710,10 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E { return sema_rewrap_var(context, left->identifier_expr.decl); } + if (left->expr_kind == EXPR_BITACCESS) + { + expr->expr_kind = EXPR_BITASSIGN; + } return true; } @@ -3824,6 +3840,10 @@ static bool sema_expr_analyse_common_assign(Context *context, Expr *expr, Expr * } } + if (left->expr_kind == EXPR_BITACCESS) + { + expr->expr_kind = EXPR_BITASSIGN; + } // 7. Assign type expr->type = left->type; return true; @@ -3892,6 +3912,10 @@ static bool sema_expr_analyse_add_sub_assign(Context *context, Expr *expr, Expr return false; } REMINDER("Check if can remove"); + if (left->expr_kind == EXPR_BITACCESS) + { + expr->expr_kind = EXPR_BITASSIGN; + } expr->type = type_get_opt_fail(expr->type, failable); return true; } @@ -4423,6 +4447,10 @@ static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *l // 5. Set the type using the lhs side. + if (left->expr_kind == EXPR_BITACCESS) + { + expr->expr_kind = EXPR_BITASSIGN; + } expr->type = type_get_opt_fail(left->type, failable); return true; } @@ -4773,6 +4801,9 @@ static bool sema_take_addr_of(Expr *inner) case EXPR_TYPEINFO: SEMA_ERROR(inner, "It is not possible to take the address of a type."); return false; + case EXPR_BITACCESS: + SEMA_ERROR(inner, "You cannot take the address of a bitstruct member."); + return false; default: break; } @@ -5049,6 +5080,10 @@ static bool unclear_op_precedence(Expr *left_side, Expr * main_expr, Expr *right return false; } +static inline bool sema_expr_analyse_bitassign(Context *context, Expr *expr) +{ + TODO +} static inline bool sema_expr_analyse_binary(Context *context, Expr *expr) { assert(expr->resolve_status == RESOLVE_RUNNING); @@ -6235,6 +6270,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Expr *expr) return sema_expr_analyse_rethrow(context, expr); case EXPR_CONST: return true; + case EXPR_BITASSIGN: + return sema_expr_analyse_bitassign(context, expr); case EXPR_BINARY: return sema_expr_analyse_binary(context, expr); case EXPR_TERNARY: @@ -6255,6 +6292,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Expr *expr) return sema_expr_analyse_subscript(context, expr); case EXPR_GROUP: return sema_expr_analyse_group(context, expr); + case EXPR_BITACCESS: + UNREACHABLE case EXPR_ACCESS: return sema_expr_analyse_access(context, expr); case EXPR_INITIALIZER_LIST: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 086c94964..23e643edf 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -135,6 +135,8 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_INLINE] = kw_inline; attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("noinline"); attribute_list[ATTRIBUTE_OPAQUE] = KW_DEF("opaque"); + attribute_list[ATTRIBUTE_BIGENDIAN] = KW_DEF("bigendian"); + attribute_list[ATTRIBUTE_LITTLEENDIAN] = KW_DEF("littleendian"); attribute_list[ATTRIBUTE_NORETURN] = KW_DEF("noreturn"); attribute_list[ATTRIBUTE_SECTION] = KW_DEF("section"); attribute_list[ATTRIBUTE_EXTNAME] = KW_DEF("extname"); diff --git a/src/compiler/types.c b/src/compiler/types.c index d9f4ed26a..5406d55ea 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1535,6 +1535,7 @@ bool type_may_have_sub_elements(Type *type) case TYPE_STRUCT: case TYPE_ENUM: case TYPE_ERRTYPE: + case TYPE_BITSTRUCT: return true; default: return false; diff --git a/src/utils/lib.h b/src/utils/lib.h index ec5e15e64..e17eb453f 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -262,6 +262,12 @@ static inline int hex_nibble(char c) return conv[(unsigned char)c]; } +static inline char nibble_hex(int c) +{ + static const char *conv = "0123456789ABCDEF"; + return conv[c]; +} + static inline bool is_whitespace(char c) { switch (c) diff --git a/test/test_suite/bitstruct/address_of_bitstruct.c3 b/test/test_suite/bitstruct/address_of_bitstruct.c3 new file mode 100644 index 000000000..7fe1d41ba --- /dev/null +++ b/test/test_suite/bitstruct/address_of_bitstruct.c3 @@ -0,0 +1,15 @@ +bitstruct BitField : char +{ + int a : 0..2; + int b : 4..5; + int c : 6..7; +} + +fn void test() +{ + BitField x; + BitField* z = &x; + x.a; + z.a; + &x.a; // #error: You cannot take the address of a bitstruct member +} \ No newline at end of file diff --git a/test/test_suite/bitstruct/array_with_boolean.c3t b/test/test_suite/bitstruct/array_with_boolean.c3t new file mode 100644 index 000000000..4e05a0ebd --- /dev/null +++ b/test/test_suite/bitstruct/array_with_boolean.c3t @@ -0,0 +1,71 @@ +// #target: x64-darwin + +module foo; + +bitstruct BitField : char[3] +{ + int a : 0..2; + int b : 3..8; + int c : 9..18; + bool d : 19; + bool e : 20; +} + +extern fn void printf(char*, ...); + +fn void main() +{ + BitField xx = { 2, 3, 15, true, false }; + BitField xy = { 2, 3, 15, false, true }; + printf("%x, %x, %x, %d, %d\n", xx.a, xx.b, xx.c, xx.d, xx.e); +} + + +/* #expect: foo.ll + +define void @main() #0 { +entry: + %xx = alloca [3 x i8], align 1 + %xy = alloca [3 x i8], align 1 + store [3 x i8] c"\1A\1E\08", [3 x i8]* %xx, align 1 + store [3 x i8] c"\1A\1E\10", [3 x i8]* %xy, align 1 + %0 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %1 = load i8, i8* %0, align 1 + %2 = zext i8 %1 to i32 + %3 = shl i32 %2, 29 + %4 = ashr i32 %3, 29 + %5 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %6 = load i8, i8* %5, align 1 + %7 = zext i8 %6 to i32 + %8 = lshr i32 %7, 3 + %9 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %10 = load i8, i8* %9, align 1 + %11 = zext i8 %10 to i32 + %12 = shl i32 %11, 5 + %13 = or i32 %12, %8 + %14 = shl i32 %13, 26 + %15 = ashr i32 %14, 26 + %16 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %17 = load i8, i8* %16, align 1 + %18 = zext i8 %17 to i32 + %19 = lshr i32 %18, 1 + %20 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %21 = load i8, i8* %20, align 1 + %22 = zext i8 %21 to i32 + %23 = shl i32 %22, 7 + %24 = or i32 %23, %19 + %25 = shl i32 %24, 22 + %26 = ashr i32 %25, 22 + %27 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %28 = load i8, i8* %27, align 1 + %29 = lshr i8 %28, 3 + %30 = trunc i8 %29 to i1 + %boolsi = zext i1 %30 to i32 + %31 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %32 = load i8, i8* %31, align 1 + %33 = lshr i8 %32, 4 + %34 = trunc i8 %33 to i1 + %boolsi1 = zext i1 %34 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([20 x i8], [20 x i8]* @.str, i32 0, i32 0), i32 %4, i32 %15, i32 %26, i32 %boolsi, i32 %boolsi1) + ret void +} \ No newline at end of file diff --git a/test/test_suite/bitstruct/bitfield_access.c3t b/test/test_suite/bitstruct/bitfield_access.c3t new file mode 100644 index 000000000..97bc9e1c9 --- /dev/null +++ b/test/test_suite/bitstruct/bitfield_access.c3t @@ -0,0 +1,180 @@ +// #target: x64-darwin + +module foo; + +bitstruct BitField : char +{ + int a : 0..2; + int b : 4..5; + int c : 6..7; +} + +bitstruct BitFieldI : ushort +{ + int a : 0..4; + int b : 5..10; + int c : 11..15; +} + + +struct Packet +{ + bitstruct : int + { + int a : 0..2; + int b : 3..5; + int c : 6..10; + } + int packet_id; +} + +bitstruct BitField3 : char[3] +{ + int a : 0..2; + int b : 3..8; + int c : 9..18; + int d : 19..23; +} + +bitstruct BitField3u : char[3] +{ + uint a : 0..2; + uint b : 3..8; + uint c : 9..18; + uint d : 19..23; +} + +bitstruct BitField4 : char[3] +{ + int a : 0..2; + int b : 3..7; + int c : 8..15; + int d : 16..19; +} + +bitstruct BitFieldCross : char[5] +{ + uint a : 5..22; +} + +BitField c = { 2, 4, 5 }; + +extern fn void printf(char*, ...); + +fn void main() +{ + BitField b = { 5, -3, 1 }; + BitFieldI c1 = { 5, 0, 0 }; + BitFieldI c2 = { 0, 3, 0 }; + BitFieldI c3 = { 0, 0, 9 }; + BitFieldI c4 = { -5, 7, 9 }; + BitFieldI c5 = { -5, 0, 0 }; + BitFieldI c6 = { 5, 0, 0 }; + BitFieldI c7 = { 0, 0, 5 }; + + BitField3 e1 = { 3, 1, 3, 4 }; + BitField3 e2 = { 4, 1, 3, 4 }; + BitField3 e3 = { 5, 1, 3, 4 }; + BitField d = { }; + + printf("%d\n", e1.a); + printf("%d\n", e2.a); + printf("%d\n", e3.a); + + BitField3u z1 = { 3, 1, 3, 4 }; + BitField3u z2 = { 4, 1, 3, 4 }; + BitField3u z3 = { 9, 1, 3, 4 }; + printf("%u\n", z1.a); + printf("%u\n", z2.a); + printf("%u\n", z3.a); + BitFieldCross xx = { 0x12345678 }; + printf("%x\n", xx.a); +} + +/* #expect: foo.ll + +define void @main() #0 { +entry: + %b = alloca i8, align 1 + %c1 = alloca i16, align 2 + %c2 = alloca i16, align 2 + %c3 = alloca i16, align 2 + %c4 = alloca i16, align 2 + %c5 = alloca i16, align 2 + %c6 = alloca i16, align 2 + %c7 = alloca i16, align 2 + %e1 = alloca [3 x i8], align 1 + %e2 = alloca [3 x i8], align 1 + %e3 = alloca [3 x i8], align 1 + %d = alloca i8, align 1 + %z1 = alloca [3 x i8], align 1 + %z2 = alloca [3 x i8], align 1 + %z3 = alloca [3 x i8], align 1 + %xx = alloca [5 x i8], align 1 + store i8 85, i8* %b, align 1 + store i16 5, i16* %c1, align 2 + store i16 96, i16* %c2, align 2 + store i16 18432, i16* %c3, align 2 + store i16 18683, i16* %c4, align 2 + store i16 27, i16* %c5, align 2 + store i16 5, i16* %c6, align 2 + store i16 10240, i16* %c7, align 2 + store [3 x i8] c"\0B\06 ", [3 x i8]* %e1, align 1 + store [3 x i8] c"\0C\06 ", [3 x i8]* %e2, align 1 + store [3 x i8] c"\0D\06 ", [3 x i8]* %e3, align 1 + store i8 0, i8* %d, align 1 + %0 = getelementptr inbounds [3 x i8], [3 x i8]* %e1, i64 0, i64 0 + %1 = load i8, i8* %0, align 1 + %2 = zext i8 %1 to i32 + %3 = shl i32 %2, 29 + %4 = ashr i32 %3, 29 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %4) + %5 = getelementptr inbounds [3 x i8], [3 x i8]* %e2, i64 0, i64 0 + %6 = load i8, i8* %5, align 1 + %7 = zext i8 %6 to i32 + %8 = shl i32 %7, 29 + %9 = ashr i32 %8, 29 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %9) + %10 = getelementptr inbounds [3 x i8], [3 x i8]* %e3, i64 0, i64 0 + %11 = load i8, i8* %10, align 1 + %12 = zext i8 %11 to i32 + %13 = shl i32 %12, 29 + %14 = ashr i32 %13, 29 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i32 0, i32 0), i32 %14) + store [3 x i8] c"\0B\06 ", [3 x i8]* %z1, align 1 + store [3 x i8] c"\0C\06 ", [3 x i8]* %z2, align 1 + store [3 x i8] c"\09\06 ", [3 x i8]* %z3, align 1 + %15 = getelementptr inbounds [3 x i8], [3 x i8]* %z1, i64 0, i64 0 + %16 = load i8, i8* %15, align 1 + %17 = zext i8 %16 to i32 + %18 = and i32 7, %17 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.3, i32 0, i32 0), i32 %18) + %19 = getelementptr inbounds [3 x i8], [3 x i8]* %z2, i64 0, i64 0 + %20 = load i8, i8* %19, align 1 + %21 = zext i8 %20 to i32 + %22 = and i32 7, %21 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.4, i32 0, i32 0), i32 %22) + %23 = getelementptr inbounds [3 x i8], [3 x i8]* %z3, i64 0, i64 0 + %24 = load i8, i8* %23, align 1 + %25 = zext i8 %24 to i32 + %26 = and i32 7, %25 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.5, i32 0, i32 0), i32 %26) + store [5 x i8] c"\00\CF\0A\00\00", [5 x i8]* %xx, align 1 + %27 = getelementptr inbounds [5 x i8], [5 x i8]* %xx, i64 0, i64 0 + %28 = load i8, i8* %27, align 1 + %29 = zext i8 %28 to i32 + %30 = lshr i32 %29, 5 + %31 = getelementptr inbounds [5 x i8], [5 x i8]* %xx, i64 0, i64 1 + %32 = load i8, i8* %31, align 1 + %33 = zext i8 %32 to i32 + %34 = shl i32 %33, 3 + %35 = or i32 %34, %30 + %36 = getelementptr inbounds [5 x i8], [5 x i8]* %xx, i64 0, i64 2 + %37 = load i8, i8* %36, align 1 + %38 = zext i8 %37 to i32 + %39 = shl i32 %38, 11 + %40 = or i32 %39, %35 + %41 = and i32 262143, %40 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.6, i32 0, i32 0), i32 %41) + ret void +} \ No newline at end of file diff --git a/test/test_suite/bitstruct/bitstruct_access_signed.c3t b/test/test_suite/bitstruct/bitstruct_access_signed.c3t new file mode 100644 index 000000000..4cce549c4 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_access_signed.c3t @@ -0,0 +1,92 @@ +// #target: x64-darwin + +module foo; + +bitstruct BitFieldCross : char[3] +{ + uint d : 0..4; + int a : 5..22; + uint c : 23..23; +} + +bitstruct BitFieldCrossU : char[3] +{ + uint d : 0..4; + uint a : 5..22; + uint c : 23..23; +} +extern fn void printf(char*, ...); + +fn void main() +{ + BitFieldCross xx = { 0, -177, 0 }; + printf("%d\n", xx.a); + xx = { 0xff, -177, 0xff }; + printf("%d\n", xx.a); + BitFieldCrossU xxu = { 0xff, 0x12345678, 0xff }; + printf("%x\n", xxu.a); + +} + +/* #expect: foo.ll + +define void @main() #0 { +entry: + %xx = alloca [3 x i8], align 1 + %xxu = alloca [3 x i8], align 1 + store [3 x i8] c"\E0\E9\7F", [3 x i8]* %xx, align 1 + %0 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %1 = load i8, i8* %0, align 1 + %2 = zext i8 %1 to i32 + %3 = lshr i32 %2, 5 + %4 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %5 = load i8, i8* %4, align 1 + %6 = zext i8 %5 to i32 + %7 = shl i32 %6, 3 + %8 = or i32 %7, %3 + %9 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %10 = load i8, i8* %9, align 1 + %11 = zext i8 %10 to i32 + %12 = shl i32 %11, 11 + %13 = or i32 %12, %8 + %14 = shl i32 %13, 14 + %15 = ashr i32 %14, 14 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %15) + store [3 x i8] c"\FF\E9\FF", [3 x i8]* %xx, align 1 + %16 = load [3 x i8], [3 x i8]* %xx, align 1 + %17 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %18 = load i8, i8* %17, align 1 + %19 = zext i8 %18 to i32 + %20 = lshr i32 %19, 5 + %21 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %22 = load i8, i8* %21, align 1 + %23 = zext i8 %22 to i32 + %24 = shl i32 %23, 3 + %25 = or i32 %24, %20 + %26 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %27 = load i8, i8* %26, align 1 + %28 = zext i8 %27 to i32 + %29 = shl i32 %28, 11 + %30 = or i32 %29, %25 + %31 = shl i32 %30, 14 + %32 = ashr i32 %31, 14 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %32) + store [3 x i8] c"\1F\CF\8A", [3 x i8]* %xxu, align 1 + %33 = getelementptr inbounds [3 x i8], [3 x i8]* %xxu, i64 0, i64 0 + %34 = load i8, i8* %33, align 1 + %35 = zext i8 %34 to i32 + %36 = lshr i32 %35, 5 + %37 = getelementptr inbounds [3 x i8], [3 x i8]* %xxu, i64 0, i64 1 + %38 = load i8, i8* %37, align 1 + %39 = zext i8 %38 to i32 + %40 = shl i32 %39, 3 + %41 = or i32 %40, %36 + %42 = getelementptr inbounds [3 x i8], [3 x i8]* %xxu, i64 0, i64 2 + %43 = load i8, i8* %42, align 1 + %44 = zext i8 %43 to i32 + %45 = shl i32 %44, 11 + %46 = or i32 %45, %41 + %47 = and i32 262143, %46 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i32 0, i32 0), i32 %47) + ret void +} diff --git a/test/test_suite/bitstruct/bitstruct_arrays.c3t b/test/test_suite/bitstruct/bitstruct_arrays.c3t new file mode 100644 index 000000000..3a8860c91 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_arrays.c3t @@ -0,0 +1,1146 @@ +// #target: x64-darwin + +module foo; + +bitstruct BitField : long +{ + int a : 0..2; + int b : 3..8; + int c : 9..18; + bool d : 19; + bool e : 20; +} + +bitstruct BitField2 : char[3] +{ + int a : 0..2; + int b : 3..8; + int c : 9..18; + bool d : 19; + bool e : 20; +} + +bitstruct BitField3 : char[3] +{ + int a : 1..3; + int b : 4..9; + int c : 10..19; + bool d : 20; + bool e : 21; +} + +extern fn void printf(char*, ...); + +fn void main() +{ + test1(); + test2(); + test3(); +} +fn void test1() +{ + BitField xx = { 2, 3, 15, true, false }; + xx.a = 3; + printf("%d, %d\n", xx.a, xx.b); + xx.a -= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b *= 2; + printf("%d, %d\n", xx.a, xx.b); + xx.b ^= 4; + printf("%d, %d\n", xx.a, xx.b); + xx.b |= 4; + printf("%d, %d\n", xx.a, xx.b); + xx.b <<= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b >>= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b /= 2; + printf("%d, %d\n", xx.a, xx.b); + xx.b %= 2; + printf("%d, %d\n", xx.a, xx.b); + xx.e = true; + printf("%d\n", xx.e); + xx.e = false; + printf("%d\n", xx.e); +} + +fn void test2() +{ + BitField2 xx = { 2, 3, 15, false, false }; + xx.a = 3; + printf("%d, %d\n", xx.a, xx.b); + xx.a -= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b *= 2; + printf("%d, %d\n", xx.a, xx.b); + xx.b ^= 4; + printf("%d, %d\n", xx.a, xx.b); + xx.b |= 4; + printf("%d, %d\n", xx.a, xx.b); + xx.b <<= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b >>= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b /= 2; + printf("%d, %d\n", xx.a, xx.b); + xx.b %= 2; + printf("%d, %d\n", xx.a, xx.b); + printf("..%d\n", xx.e); + xx.e = true; + printf("%d\n", xx.e); + xx.e = false; + printf("%d\n", xx.e); +} + +fn void test3() +{ + BitField3 xx = { 2, 3, 15, false, false }; + xx.a = 3; + printf("%d, %d\n", xx.a, xx.b); + xx.a -= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b *= 2; + printf("%d, %d\n", xx.a, xx.b); + xx.b ^= 4; + printf("%d, %d\n", xx.a, xx.b); + xx.b |= 4; + printf("%d, %d\n", xx.a, xx.b); + xx.b <<= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b >>= 1; + printf("%d, %d\n", xx.a, xx.b); + xx.b /= 2; + printf("%d, %d\n", xx.a, xx.b); + xx.b %= 2; + printf("%d, %d\n", xx.a, xx.b); + printf("..%d\n", xx.e); + xx.e = true; + printf("%d\n", xx.e); + xx.e = false; + printf("%d\n", xx.e); +} + +/* #expect: foo.ll + +define void @main() #0 { +entry: + call void @foo.test1() + call void @foo.test2() + call void @foo.test3() + ret void +} + +; Function Attrs: nounwind +define void @foo.test1() #0 { +entry: + %xx = alloca i64, align 8 + store i64 531994, i64* %xx, align 8 + %0 = load i64, i64* %xx, align 8 + %1 = and i64 %0, -8 + %2 = or i64 %1, 3 + store i64 %2, i64* %xx, align 8 + %3 = load i64, i64* %xx, align 8 + %4 = shl i64 %3, 61 + %5 = ashr i64 %4, 61 + %6 = trunc i64 %5 to i32 + %7 = load i64, i64* %xx, align 8 + %8 = shl i64 %7, 55 + %9 = ashr i64 %8, 58 + %10 = trunc i64 %9 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 %6, i32 %10) + %11 = load i64, i64* %xx, align 8 + %12 = shl i64 %11, 61 + %13 = ashr i64 %12, 61 + %14 = trunc i64 %13 to i32 + %sub = sub i32 %14, 1 + %15 = load i64, i64* %xx, align 8 + %16 = zext i32 %sub to i64 + %17 = and i64 %16, 7 + %18 = and i64 %15, -8 + %19 = or i64 %18, %17 + store i64 %19, i64* %xx, align 8 + %20 = load i64, i64* %xx, align 8 + %21 = shl i64 %20, 61 + %22 = ashr i64 %21, 61 + %23 = trunc i64 %22 to i32 + %24 = load i64, i64* %xx, align 8 + %25 = shl i64 %24, 55 + %26 = ashr i64 %25, 58 + %27 = trunc i64 %26 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.1, i32 0, i32 0), i32 %23, i32 %27) + %28 = load i64, i64* %xx, align 8 + %29 = shl i64 %28, 55 + %30 = ashr i64 %29, 58 + %31 = trunc i64 %30 to i32 + %mul = mul i32 %31, 2 + %32 = load i64, i64* %xx, align 8 + %33 = zext i32 %mul to i64 + %34 = shl i64 %33, 3 + %35 = and i64 %34, 504 + %36 = and i64 %32, -505 + %37 = or i64 %36, %35 + store i64 %37, i64* %xx, align 8 + %38 = load i64, i64* %xx, align 8 + %39 = shl i64 %38, 61 + %40 = ashr i64 %39, 61 + %41 = trunc i64 %40 to i32 + %42 = load i64, i64* %xx, align 8 + %43 = shl i64 %42, 55 + %44 = ashr i64 %43, 58 + %45 = trunc i64 %44 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i32 0, i32 0), i32 %41, i32 %45) + %46 = load i64, i64* %xx, align 8 + %47 = shl i64 %46, 55 + %48 = ashr i64 %47, 58 + %49 = trunc i64 %48 to i32 + %xor = xor i32 %49, 4 + %50 = load i64, i64* %xx, align 8 + %51 = zext i32 %xor to i64 + %52 = shl i64 %51, 3 + %53 = and i64 %52, 504 + %54 = and i64 %50, -505 + %55 = or i64 %54, %53 + store i64 %55, i64* %xx, align 8 + %56 = load i64, i64* %xx, align 8 + %57 = shl i64 %56, 61 + %58 = ashr i64 %57, 61 + %59 = trunc i64 %58 to i32 + %60 = load i64, i64* %xx, align 8 + %61 = shl i64 %60, 55 + %62 = ashr i64 %61, 58 + %63 = trunc i64 %62 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i32 0, i32 0), i32 %59, i32 %63) + %64 = load i64, i64* %xx, align 8 + %65 = shl i64 %64, 55 + %66 = ashr i64 %65, 58 + %67 = trunc i64 %66 to i32 + %or = or i32 %67, 4 + %68 = load i64, i64* %xx, align 8 + %69 = zext i32 %or to i64 + %70 = shl i64 %69, 3 + %71 = and i64 %70, 504 + %72 = and i64 %68, -505 + %73 = or i64 %72, %71 + store i64 %73, i64* %xx, align 8 + %74 = load i64, i64* %xx, align 8 + %75 = shl i64 %74, 61 + %76 = ashr i64 %75, 61 + %77 = trunc i64 %76 to i32 + %78 = load i64, i64* %xx, align 8 + %79 = shl i64 %78, 55 + %80 = ashr i64 %79, 58 + %81 = trunc i64 %80 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.4, i32 0, i32 0), i32 %77, i32 %81) + %82 = load i64, i64* %xx, align 8 + %83 = shl i64 %82, 55 + %84 = ashr i64 %83, 58 + %85 = trunc i64 %84 to i32 + %shl = shl i32 %85, 1 + %86 = freeze i32 %shl + %87 = load i64, i64* %xx, align 8 + %88 = zext i32 %86 to i64 + %89 = shl i64 %88, 3 + %90 = and i64 %89, 504 + %91 = and i64 %87, -505 + %92 = or i64 %91, %90 + store i64 %92, i64* %xx, align 8 + %93 = load i64, i64* %xx, align 8 + %94 = shl i64 %93, 61 + %95 = ashr i64 %94, 61 + %96 = trunc i64 %95 to i32 + %97 = load i64, i64* %xx, align 8 + %98 = shl i64 %97, 55 + %99 = ashr i64 %98, 58 + %100 = trunc i64 %99 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.5, i32 0, i32 0), i32 %96, i32 %100) + %101 = load i64, i64* %xx, align 8 + %102 = shl i64 %101, 55 + %103 = ashr i64 %102, 58 + %104 = trunc i64 %103 to i32 + %ashr = ashr i32 %104, 1 + %105 = freeze i32 %ashr + %106 = load i64, i64* %xx, align 8 + %107 = zext i32 %105 to i64 + %108 = shl i64 %107, 3 + %109 = and i64 %108, 504 + %110 = and i64 %106, -505 + %111 = or i64 %110, %109 + store i64 %111, i64* %xx, align 8 + %112 = load i64, i64* %xx, align 8 + %113 = shl i64 %112, 61 + %114 = ashr i64 %113, 61 + %115 = trunc i64 %114 to i32 + %116 = load i64, i64* %xx, align 8 + %117 = shl i64 %116, 55 + %118 = ashr i64 %117, 58 + %119 = trunc i64 %118 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.6, i32 0, i32 0), i32 %115, i32 %119) + %120 = load i64, i64* %xx, align 8 + %121 = shl i64 %120, 55 + %122 = ashr i64 %121, 58 + %123 = trunc i64 %122 to i32 + %sdiv = sdiv i32 %123, 2 + %124 = load i64, i64* %xx, align 8 + %125 = zext i32 %sdiv to i64 + %126 = shl i64 %125, 3 + %127 = and i64 %126, 504 + %128 = and i64 %124, -505 + %129 = or i64 %128, %127 + store i64 %129, i64* %xx, align 8 + %130 = load i64, i64* %xx, align 8 + %131 = shl i64 %130, 61 + %132 = ashr i64 %131, 61 + %133 = trunc i64 %132 to i32 + %134 = load i64, i64* %xx, align 8 + %135 = shl i64 %134, 55 + %136 = ashr i64 %135, 58 + %137 = trunc i64 %136 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.7, i32 0, i32 0), i32 %133, i32 %137) + %138 = load i64, i64* %xx, align 8 + %139 = shl i64 %138, 55 + %140 = ashr i64 %139, 58 + %141 = trunc i64 %140 to i32 + %smod = srem i32 %141, 2 + %142 = load i64, i64* %xx, align 8 + %143 = zext i32 %smod to i64 + %144 = shl i64 %143, 3 + %145 = and i64 %144, 504 + %146 = and i64 %142, -505 + %147 = or i64 %146, %145 + store i64 %147, i64* %xx, align 8 + %148 = load i64, i64* %xx, align 8 + %149 = shl i64 %148, 61 + %150 = ashr i64 %149, 61 + %151 = trunc i64 %150 to i32 + %152 = load i64, i64* %xx, align 8 + %153 = shl i64 %152, 55 + %154 = ashr i64 %153, 58 + %155 = trunc i64 %154 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.8, i32 0, i32 0), i32 %151, i32 %155) + %156 = load i64, i64* %xx, align 8 + %157 = and i64 %156, -1048577 + %158 = or i64 %157, 1048576 + store i64 %158, i64* %xx, align 8 + %159 = load i64, i64* %xx, align 8 + %160 = lshr i64 %159, 20 + %161 = trunc i64 %160 to i8 + %162 = trunc i8 %161 to i1 + %boolsi = zext i1 %162 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.9, i32 0, i32 0), i32 %boolsi) + %163 = load i64, i64* %xx, align 8 + %164 = and i64 %163, -1048577 + store i64 %164, i64* %xx, align 8 + %165 = load i64, i64* %xx, align 8 + %166 = lshr i64 %165, 20 + %167 = trunc i64 %166 to i8 + %168 = trunc i8 %167 to i1 + %boolsi1 = zext i1 %168 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.10, i32 0, i32 0), i32 %boolsi1) + ret void +} + +; Function Attrs: nounwind +define void @foo.test2() #0 { +entry: + %xx = alloca [3 x i8], align 1 + store [3 x i8] c"\1A\1E\00", [3 x i8]* %xx, align 1 + %0 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %1 = load i8, i8* %0, align 1 + %2 = and i8 %1, -8 + %3 = or i8 %2, 3 + store i8 %3, i8* %0, align 1 + %4 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %5 = load i8, i8* %4, align 1 + %6 = zext i8 %5 to i32 + %7 = shl i32 %6, 29 + %8 = ashr i32 %7, 29 + %9 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %10 = load i8, i8* %9, align 1 + %11 = zext i8 %10 to i32 + %12 = lshr i32 %11, 3 + %13 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %14 = load i8, i8* %13, align 1 + %15 = zext i8 %14 to i32 + %16 = shl i32 %15, 5 + %17 = or i32 %16, %12 + %18 = shl i32 %17, 26 + %19 = ashr i32 %18, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.11, i32 0, i32 0), i32 %8, i32 %19) + %20 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %21 = load i8, i8* %20, align 1 + %22 = zext i8 %21 to i32 + %23 = shl i32 %22, 29 + %24 = ashr i32 %23, 29 + %sub = sub i32 %24, 1 + %25 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %26 = trunc i32 %sub to i8 + %27 = and i8 %26, 7 + %28 = load i8, i8* %25, align 1 + %29 = and i8 %28, -8 + %30 = or i8 %29, %27 + store i8 %30, i8* %25, align 1 + %31 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %32 = load i8, i8* %31, align 1 + %33 = zext i8 %32 to i32 + %34 = shl i32 %33, 29 + %35 = ashr i32 %34, 29 + %36 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %37 = load i8, i8* %36, align 1 + %38 = zext i8 %37 to i32 + %39 = lshr i32 %38, 3 + %40 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %41 = load i8, i8* %40, align 1 + %42 = zext i8 %41 to i32 + %43 = shl i32 %42, 5 + %44 = or i32 %43, %39 + %45 = shl i32 %44, 26 + %46 = ashr i32 %45, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.12, i32 0, i32 0), i32 %35, i32 %46) + %47 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %48 = load i8, i8* %47, align 1 + %49 = zext i8 %48 to i32 + %50 = lshr i32 %49, 3 + %51 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %52 = load i8, i8* %51, align 1 + %53 = zext i8 %52 to i32 + %54 = shl i32 %53, 5 + %55 = or i32 %54, %50 + %56 = shl i32 %55, 26 + %57 = ashr i32 %56, 26 + %mul = mul i32 %57, 2 + %58 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %59 = shl i32 %mul, 3 + %60 = trunc i32 %59 to i8 + %61 = load i8, i8* %58, align 1 + %62 = and i8 %61, 7 + %63 = or i8 %62, %60 + store i8 %63, i8* %58, align 1 + %64 = lshr i32 %mul, 5 + %65 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %66 = trunc i32 %64 to i8 + %67 = and i8 %66, 1 + %68 = load i8, i8* %65, align 1 + %69 = and i8 %68, -2 + %70 = or i8 %69, %67 + store i8 %70, i8* %65, align 1 + %71 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %72 = load i8, i8* %71, align 1 + %73 = zext i8 %72 to i32 + %74 = shl i32 %73, 29 + %75 = ashr i32 %74, 29 + %76 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %77 = load i8, i8* %76, align 1 + %78 = zext i8 %77 to i32 + %79 = lshr i32 %78, 3 + %80 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %81 = load i8, i8* %80, align 1 + %82 = zext i8 %81 to i32 + %83 = shl i32 %82, 5 + %84 = or i32 %83, %79 + %85 = shl i32 %84, 26 + %86 = ashr i32 %85, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.13, i32 0, i32 0), i32 %75, i32 %86) + %87 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %88 = load i8, i8* %87, align 1 + %89 = zext i8 %88 to i32 + %90 = lshr i32 %89, 3 + %91 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %92 = load i8, i8* %91, align 1 + %93 = zext i8 %92 to i32 + %94 = shl i32 %93, 5 + %95 = or i32 %94, %90 + %96 = shl i32 %95, 26 + %97 = ashr i32 %96, 26 + %xor = xor i32 %97, 4 + %98 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %99 = shl i32 %xor, 3 + %100 = trunc i32 %99 to i8 + %101 = load i8, i8* %98, align 1 + %102 = and i8 %101, 7 + %103 = or i8 %102, %100 + store i8 %103, i8* %98, align 1 + %104 = lshr i32 %xor, 5 + %105 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %106 = trunc i32 %104 to i8 + %107 = and i8 %106, 1 + %108 = load i8, i8* %105, align 1 + %109 = and i8 %108, -2 + %110 = or i8 %109, %107 + store i8 %110, i8* %105, align 1 + %111 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %112 = load i8, i8* %111, align 1 + %113 = zext i8 %112 to i32 + %114 = shl i32 %113, 29 + %115 = ashr i32 %114, 29 + %116 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %117 = load i8, i8* %116, align 1 + %118 = zext i8 %117 to i32 + %119 = lshr i32 %118, 3 + %120 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %121 = load i8, i8* %120, align 1 + %122 = zext i8 %121 to i32 + %123 = shl i32 %122, 5 + %124 = or i32 %123, %119 + %125 = shl i32 %124, 26 + %126 = ashr i32 %125, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.14, i32 0, i32 0), i32 %115, i32 %126) + %127 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %128 = load i8, i8* %127, align 1 + %129 = zext i8 %128 to i32 + %130 = lshr i32 %129, 3 + %131 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %132 = load i8, i8* %131, align 1 + %133 = zext i8 %132 to i32 + %134 = shl i32 %133, 5 + %135 = or i32 %134, %130 + %136 = shl i32 %135, 26 + %137 = ashr i32 %136, 26 + %or = or i32 %137, 4 + %138 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %139 = shl i32 %or, 3 + %140 = trunc i32 %139 to i8 + %141 = load i8, i8* %138, align 1 + %142 = and i8 %141, 7 + %143 = or i8 %142, %140 + store i8 %143, i8* %138, align 1 + %144 = lshr i32 %or, 5 + %145 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %146 = trunc i32 %144 to i8 + %147 = and i8 %146, 1 + %148 = load i8, i8* %145, align 1 + %149 = and i8 %148, -2 + %150 = or i8 %149, %147 + store i8 %150, i8* %145, align 1 + %151 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %152 = load i8, i8* %151, align 1 + %153 = zext i8 %152 to i32 + %154 = shl i32 %153, 29 + %155 = ashr i32 %154, 29 + %156 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %157 = load i8, i8* %156, align 1 + %158 = zext i8 %157 to i32 + %159 = lshr i32 %158, 3 + %160 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %161 = load i8, i8* %160, align 1 + %162 = zext i8 %161 to i32 + %163 = shl i32 %162, 5 + %164 = or i32 %163, %159 + %165 = shl i32 %164, 26 + %166 = ashr i32 %165, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.15, i32 0, i32 0), i32 %155, i32 %166) + %167 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %168 = load i8, i8* %167, align 1 + %169 = zext i8 %168 to i32 + %170 = lshr i32 %169, 3 + %171 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %172 = load i8, i8* %171, align 1 + %173 = zext i8 %172 to i32 + %174 = shl i32 %173, 5 + %175 = or i32 %174, %170 + %176 = shl i32 %175, 26 + %177 = ashr i32 %176, 26 + %shl = shl i32 %177, 1 + %178 = freeze i32 %shl + %179 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %180 = shl i32 %178, 3 + %181 = trunc i32 %180 to i8 + %182 = load i8, i8* %179, align 1 + %183 = and i8 %182, 7 + %184 = or i8 %183, %181 + store i8 %184, i8* %179, align 1 + %185 = lshr i32 %178, 5 + %186 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %187 = trunc i32 %185 to i8 + %188 = and i8 %187, 1 + %189 = load i8, i8* %186, align 1 + %190 = and i8 %189, -2 + %191 = or i8 %190, %188 + store i8 %191, i8* %186, align 1 + %192 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %193 = load i8, i8* %192, align 1 + %194 = zext i8 %193 to i32 + %195 = shl i32 %194, 29 + %196 = ashr i32 %195, 29 + %197 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %198 = load i8, i8* %197, align 1 + %199 = zext i8 %198 to i32 + %200 = lshr i32 %199, 3 + %201 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %202 = load i8, i8* %201, align 1 + %203 = zext i8 %202 to i32 + %204 = shl i32 %203, 5 + %205 = or i32 %204, %200 + %206 = shl i32 %205, 26 + %207 = ashr i32 %206, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.16, i32 0, i32 0), i32 %196, i32 %207) + %208 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %209 = load i8, i8* %208, align 1 + %210 = zext i8 %209 to i32 + %211 = lshr i32 %210, 3 + %212 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %213 = load i8, i8* %212, align 1 + %214 = zext i8 %213 to i32 + %215 = shl i32 %214, 5 + %216 = or i32 %215, %211 + %217 = shl i32 %216, 26 + %218 = ashr i32 %217, 26 + %ashr = ashr i32 %218, 1 + %219 = freeze i32 %ashr + %220 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %221 = shl i32 %219, 3 + %222 = trunc i32 %221 to i8 + %223 = load i8, i8* %220, align 1 + %224 = and i8 %223, 7 + %225 = or i8 %224, %222 + store i8 %225, i8* %220, align 1 + %226 = lshr i32 %219, 5 + %227 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %228 = trunc i32 %226 to i8 + %229 = and i8 %228, 1 + %230 = load i8, i8* %227, align 1 + %231 = and i8 %230, -2 + %232 = or i8 %231, %229 + store i8 %232, i8* %227, align 1 + %233 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %234 = load i8, i8* %233, align 1 + %235 = zext i8 %234 to i32 + %236 = shl i32 %235, 29 + %237 = ashr i32 %236, 29 + %238 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %239 = load i8, i8* %238, align 1 + %240 = zext i8 %239 to i32 + %241 = lshr i32 %240, 3 + %242 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %243 = load i8, i8* %242, align 1 + %244 = zext i8 %243 to i32 + %245 = shl i32 %244, 5 + %246 = or i32 %245, %241 + %247 = shl i32 %246, 26 + %248 = ashr i32 %247, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.17, i32 0, i32 0), i32 %237, i32 %248) + %249 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %250 = load i8, i8* %249, align 1 + %251 = zext i8 %250 to i32 + %252 = lshr i32 %251, 3 + %253 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %254 = load i8, i8* %253, align 1 + %255 = zext i8 %254 to i32 + %256 = shl i32 %255, 5 + %257 = or i32 %256, %252 + %258 = shl i32 %257, 26 + %259 = ashr i32 %258, 26 + %sdiv = sdiv i32 %259, 2 + %260 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %261 = shl i32 %sdiv, 3 + %262 = trunc i32 %261 to i8 + %263 = load i8, i8* %260, align 1 + %264 = and i8 %263, 7 + %265 = or i8 %264, %262 + store i8 %265, i8* %260, align 1 + %266 = lshr i32 %sdiv, 5 + %267 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %268 = trunc i32 %266 to i8 + %269 = and i8 %268, 1 + %270 = load i8, i8* %267, align 1 + %271 = and i8 %270, -2 + %272 = or i8 %271, %269 + store i8 %272, i8* %267, align 1 + %273 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %274 = load i8, i8* %273, align 1 + %275 = zext i8 %274 to i32 + %276 = shl i32 %275, 29 + %277 = ashr i32 %276, 29 + %278 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %279 = load i8, i8* %278, align 1 + %280 = zext i8 %279 to i32 + %281 = lshr i32 %280, 3 + %282 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %283 = load i8, i8* %282, align 1 + %284 = zext i8 %283 to i32 + %285 = shl i32 %284, 5 + %286 = or i32 %285, %281 + %287 = shl i32 %286, 26 + %288 = ashr i32 %287, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.18, i32 0, i32 0), i32 %277, i32 %288) + %289 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %290 = load i8, i8* %289, align 1 + %291 = zext i8 %290 to i32 + %292 = lshr i32 %291, 3 + %293 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %294 = load i8, i8* %293, align 1 + %295 = zext i8 %294 to i32 + %296 = shl i32 %295, 5 + %297 = or i32 %296, %292 + %298 = shl i32 %297, 26 + %299 = ashr i32 %298, 26 + %smod = srem i32 %299, 2 + %300 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %301 = shl i32 %smod, 3 + %302 = trunc i32 %301 to i8 + %303 = load i8, i8* %300, align 1 + %304 = and i8 %303, 7 + %305 = or i8 %304, %302 + store i8 %305, i8* %300, align 1 + %306 = lshr i32 %smod, 5 + %307 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %308 = trunc i32 %306 to i8 + %309 = and i8 %308, 1 + %310 = load i8, i8* %307, align 1 + %311 = and i8 %310, -2 + %312 = or i8 %311, %309 + store i8 %312, i8* %307, align 1 + %313 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %314 = load i8, i8* %313, align 1 + %315 = zext i8 %314 to i32 + %316 = shl i32 %315, 29 + %317 = ashr i32 %316, 29 + %318 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %319 = load i8, i8* %318, align 1 + %320 = zext i8 %319 to i32 + %321 = lshr i32 %320, 3 + %322 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %323 = load i8, i8* %322, align 1 + %324 = zext i8 %323 to i32 + %325 = shl i32 %324, 5 + %326 = or i32 %325, %321 + %327 = shl i32 %326, 26 + %328 = ashr i32 %327, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.19, i32 0, i32 0), i32 %317, i32 %328) + %329 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %330 = load i8, i8* %329, align 1 + %331 = lshr i8 %330, 4 + %332 = trunc i8 %331 to i1 + %boolsi = zext i1 %332 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.20, i32 0, i32 0), i32 %boolsi) + %333 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %334 = load i8, i8* %333, align 1 + %335 = and i8 %334, -17 + %336 = or i8 %335, 16 + store i8 %336, i8* %333, align 1 + %337 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %338 = load i8, i8* %337, align 1 + %339 = lshr i8 %338, 4 + %340 = trunc i8 %339 to i1 + %boolsi1 = zext i1 %340 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.21, i32 0, i32 0), i32 %boolsi1) + %341 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %342 = load i8, i8* %341, align 1 + %343 = and i8 %342, -17 + store i8 %343, i8* %341, align 1 + %344 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %345 = load i8, i8* %344, align 1 + %346 = lshr i8 %345, 4 + %347 = trunc i8 %346 to i1 + %boolsi2 = zext i1 %347 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.22, i32 0, i32 0), i32 %boolsi2) + ret void +} + +; Function Attrs: nounwind +define void @foo.test3() #0 { +entry: + %xx = alloca [3 x i8], align 1 + store [3 x i8] c"4<\00", [3 x i8]* %xx, align 1 + %0 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %1 = load i8, i8* %0, align 1 + %2 = and i8 %1, -15 + %3 = or i8 %2, 6 + store i8 %3, i8* %0, align 1 + %4 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %5 = load i8, i8* %4, align 1 + %6 = zext i8 %5 to i32 + %7 = lshr i32 %6, 1 + %8 = shl i32 %7, 29 + %9 = ashr i32 %8, 29 + %10 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %11 = load i8, i8* %10, align 1 + %12 = zext i8 %11 to i32 + %13 = lshr i32 %12, 4 + %14 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %15 = load i8, i8* %14, align 1 + %16 = zext i8 %15 to i32 + %17 = shl i32 %16, 4 + %18 = or i32 %17, %13 + %19 = shl i32 %18, 26 + %20 = ashr i32 %19, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.23, i32 0, i32 0), i32 %9, i32 %20) + %21 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %22 = load i8, i8* %21, align 1 + %23 = zext i8 %22 to i32 + %24 = lshr i32 %23, 1 + %25 = shl i32 %24, 29 + %26 = ashr i32 %25, 29 + %sub = sub i32 %26, 1 + %27 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %28 = shl i32 %sub, 1 + %29 = trunc i32 %28 to i8 + %30 = and i8 %29, 15 + %31 = load i8, i8* %27, align 1 + %32 = and i8 %31, -15 + %33 = or i8 %32, %30 + store i8 %33, i8* %27, align 1 + %34 = lshr i32 %sub, 7 + %35 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %36 = load i8, i8* %35, align 1 + %37 = zext i8 %36 to i32 + %38 = lshr i32 %37, 1 + %39 = shl i32 %38, 29 + %40 = ashr i32 %39, 29 + %41 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %42 = load i8, i8* %41, align 1 + %43 = zext i8 %42 to i32 + %44 = lshr i32 %43, 4 + %45 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %46 = load i8, i8* %45, align 1 + %47 = zext i8 %46 to i32 + %48 = shl i32 %47, 4 + %49 = or i32 %48, %44 + %50 = shl i32 %49, 26 + %51 = ashr i32 %50, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.24, i32 0, i32 0), i32 %40, i32 %51) + %52 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %53 = load i8, i8* %52, align 1 + %54 = zext i8 %53 to i32 + %55 = lshr i32 %54, 4 + %56 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %57 = load i8, i8* %56, align 1 + %58 = zext i8 %57 to i32 + %59 = shl i32 %58, 4 + %60 = or i32 %59, %55 + %61 = shl i32 %60, 26 + %62 = ashr i32 %61, 26 + %mul = mul i32 %62, 2 + %63 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %64 = shl i32 %mul, 4 + %65 = trunc i32 %64 to i8 + %66 = load i8, i8* %63, align 1 + %67 = and i8 %66, 15 + %68 = or i8 %67, %65 + store i8 %68, i8* %63, align 1 + %69 = lshr i32 %mul, 4 + %70 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %71 = trunc i32 %69 to i8 + %72 = and i8 %71, 3 + %73 = load i8, i8* %70, align 1 + %74 = and i8 %73, -4 + %75 = or i8 %74, %72 + store i8 %75, i8* %70, align 1 + %76 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %77 = load i8, i8* %76, align 1 + %78 = zext i8 %77 to i32 + %79 = lshr i32 %78, 1 + %80 = shl i32 %79, 29 + %81 = ashr i32 %80, 29 + %82 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %83 = load i8, i8* %82, align 1 + %84 = zext i8 %83 to i32 + %85 = lshr i32 %84, 4 + %86 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %87 = load i8, i8* %86, align 1 + %88 = zext i8 %87 to i32 + %89 = shl i32 %88, 4 + %90 = or i32 %89, %85 + %91 = shl i32 %90, 26 + %92 = ashr i32 %91, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.25, i32 0, i32 0), i32 %81, i32 %92) + %93 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %94 = load i8, i8* %93, align 1 + %95 = zext i8 %94 to i32 + %96 = lshr i32 %95, 4 + %97 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %98 = load i8, i8* %97, align 1 + %99 = zext i8 %98 to i32 + %100 = shl i32 %99, 4 + %101 = or i32 %100, %96 + %102 = shl i32 %101, 26 + %103 = ashr i32 %102, 26 + %xor = xor i32 %103, 4 + %104 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %105 = shl i32 %xor, 4 + %106 = trunc i32 %105 to i8 + %107 = load i8, i8* %104, align 1 + %108 = and i8 %107, 15 + %109 = or i8 %108, %106 + store i8 %109, i8* %104, align 1 + %110 = lshr i32 %xor, 4 + %111 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %112 = trunc i32 %110 to i8 + %113 = and i8 %112, 3 + %114 = load i8, i8* %111, align 1 + %115 = and i8 %114, -4 + %116 = or i8 %115, %113 + store i8 %116, i8* %111, align 1 + %117 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %118 = load i8, i8* %117, align 1 + %119 = zext i8 %118 to i32 + %120 = lshr i32 %119, 1 + %121 = shl i32 %120, 29 + %122 = ashr i32 %121, 29 + %123 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %124 = load i8, i8* %123, align 1 + %125 = zext i8 %124 to i32 + %126 = lshr i32 %125, 4 + %127 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %128 = load i8, i8* %127, align 1 + %129 = zext i8 %128 to i32 + %130 = shl i32 %129, 4 + %131 = or i32 %130, %126 + %132 = shl i32 %131, 26 + %133 = ashr i32 %132, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.26, i32 0, i32 0), i32 %122, i32 %133) + %134 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %135 = load i8, i8* %134, align 1 + %136 = zext i8 %135 to i32 + %137 = lshr i32 %136, 4 + %138 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %139 = load i8, i8* %138, align 1 + %140 = zext i8 %139 to i32 + %141 = shl i32 %140, 4 + %142 = or i32 %141, %137 + %143 = shl i32 %142, 26 + %144 = ashr i32 %143, 26 + %or = or i32 %144, 4 + %145 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %146 = shl i32 %or, 4 + %147 = trunc i32 %146 to i8 + %148 = load i8, i8* %145, align 1 + %149 = and i8 %148, 15 + %150 = or i8 %149, %147 + store i8 %150, i8* %145, align 1 + %151 = lshr i32 %or, 4 + %152 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %153 = trunc i32 %151 to i8 + %154 = and i8 %153, 3 + %155 = load i8, i8* %152, align 1 + %156 = and i8 %155, -4 + %157 = or i8 %156, %154 + store i8 %157, i8* %152, align 1 + %158 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %159 = load i8, i8* %158, align 1 + %160 = zext i8 %159 to i32 + %161 = lshr i32 %160, 1 + %162 = shl i32 %161, 29 + %163 = ashr i32 %162, 29 + %164 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %165 = load i8, i8* %164, align 1 + %166 = zext i8 %165 to i32 + %167 = lshr i32 %166, 4 + %168 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %169 = load i8, i8* %168, align 1 + %170 = zext i8 %169 to i32 + %171 = shl i32 %170, 4 + %172 = or i32 %171, %167 + %173 = shl i32 %172, 26 + %174 = ashr i32 %173, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.27, i32 0, i32 0), i32 %163, i32 %174) + %175 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %176 = load i8, i8* %175, align 1 + %177 = zext i8 %176 to i32 + %178 = lshr i32 %177, 4 + %179 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %180 = load i8, i8* %179, align 1 + %181 = zext i8 %180 to i32 + %182 = shl i32 %181, 4 + %183 = or i32 %182, %178 + %184 = shl i32 %183, 26 + %185 = ashr i32 %184, 26 + %shl = shl i32 %185, 1 + %186 = freeze i32 %shl + %187 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %188 = shl i32 %186, 4 + %189 = trunc i32 %188 to i8 + %190 = load i8, i8* %187, align 1 + %191 = and i8 %190, 15 + %192 = or i8 %191, %189 + store i8 %192, i8* %187, align 1 + %193 = lshr i32 %186, 4 + %194 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %195 = trunc i32 %193 to i8 + %196 = and i8 %195, 3 + %197 = load i8, i8* %194, align 1 + %198 = and i8 %197, -4 + %199 = or i8 %198, %196 + store i8 %199, i8* %194, align 1 + %200 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %201 = load i8, i8* %200, align 1 + %202 = zext i8 %201 to i32 + %203 = lshr i32 %202, 1 + %204 = shl i32 %203, 29 + %205 = ashr i32 %204, 29 + %206 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %207 = load i8, i8* %206, align 1 + %208 = zext i8 %207 to i32 + %209 = lshr i32 %208, 4 + %210 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %211 = load i8, i8* %210, align 1 + %212 = zext i8 %211 to i32 + %213 = shl i32 %212, 4 + %214 = or i32 %213, %209 + %215 = shl i32 %214, 26 + %216 = ashr i32 %215, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.28, i32 0, i32 0), i32 %205, i32 %216) + %217 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %218 = load i8, i8* %217, align 1 + %219 = zext i8 %218 to i32 + %220 = lshr i32 %219, 4 + %221 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %222 = load i8, i8* %221, align 1 + %223 = zext i8 %222 to i32 + %224 = shl i32 %223, 4 + %225 = or i32 %224, %220 + %226 = shl i32 %225, 26 + %227 = ashr i32 %226, 26 + %ashr = ashr i32 %227, 1 + %228 = freeze i32 %ashr + %229 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %230 = shl i32 %228, 4 + %231 = trunc i32 %230 to i8 + %232 = load i8, i8* %229, align 1 + %233 = and i8 %232, 15 + %234 = or i8 %233, %231 + store i8 %234, i8* %229, align 1 + %235 = lshr i32 %228, 4 + %236 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %237 = trunc i32 %235 to i8 + %238 = and i8 %237, 3 + %239 = load i8, i8* %236, align 1 + %240 = and i8 %239, -4 + %241 = or i8 %240, %238 + store i8 %241, i8* %236, align 1 + %242 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %243 = load i8, i8* %242, align 1 + %244 = zext i8 %243 to i32 + %245 = lshr i32 %244, 1 + %246 = shl i32 %245, 29 + %247 = ashr i32 %246, 29 + %248 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %249 = load i8, i8* %248, align 1 + %250 = zext i8 %249 to i32 + %251 = lshr i32 %250, 4 + %252 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %253 = load i8, i8* %252, align 1 + %254 = zext i8 %253 to i32 + %255 = shl i32 %254, 4 + %256 = or i32 %255, %251 + %257 = shl i32 %256, 26 + %258 = ashr i32 %257, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.29, i32 0, i32 0), i32 %247, i32 %258) + %259 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %260 = load i8, i8* %259, align 1 + %261 = zext i8 %260 to i32 + %262 = lshr i32 %261, 4 + %263 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %264 = load i8, i8* %263, align 1 + %265 = zext i8 %264 to i32 + %266 = shl i32 %265, 4 + %267 = or i32 %266, %262 + %268 = shl i32 %267, 26 + %269 = ashr i32 %268, 26 + %sdiv = sdiv i32 %269, 2 + %270 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %271 = shl i32 %sdiv, 4 + %272 = trunc i32 %271 to i8 + %273 = load i8, i8* %270, align 1 + %274 = and i8 %273, 15 + %275 = or i8 %274, %272 + store i8 %275, i8* %270, align 1 + %276 = lshr i32 %sdiv, 4 + %277 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %278 = trunc i32 %276 to i8 + %279 = and i8 %278, 3 + %280 = load i8, i8* %277, align 1 + %281 = and i8 %280, -4 + %282 = or i8 %281, %279 + store i8 %282, i8* %277, align 1 + %283 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %284 = load i8, i8* %283, align 1 + %285 = zext i8 %284 to i32 + %286 = lshr i32 %285, 1 + %287 = shl i32 %286, 29 + %288 = ashr i32 %287, 29 + %289 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %290 = load i8, i8* %289, align 1 + %291 = zext i8 %290 to i32 + %292 = lshr i32 %291, 4 + %293 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %294 = load i8, i8* %293, align 1 + %295 = zext i8 %294 to i32 + %296 = shl i32 %295, 4 + %297 = or i32 %296, %292 + %298 = shl i32 %297, 26 + %299 = ashr i32 %298, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.30, i32 0, i32 0), i32 %288, i32 %299) + %300 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %301 = load i8, i8* %300, align 1 + %302 = zext i8 %301 to i32 + %303 = lshr i32 %302, 4 + %304 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %305 = load i8, i8* %304, align 1 + %306 = zext i8 %305 to i32 + %307 = shl i32 %306, 4 + %308 = or i32 %307, %303 + %309 = shl i32 %308, 26 + %310 = ashr i32 %309, 26 + %smod = srem i32 %310, 2 + %311 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %312 = shl i32 %smod, 4 + %313 = trunc i32 %312 to i8 + %314 = load i8, i8* %311, align 1 + %315 = and i8 %314, 15 + %316 = or i8 %315, %313 + store i8 %316, i8* %311, align 1 + %317 = lshr i32 %smod, 4 + %318 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %319 = trunc i32 %317 to i8 + %320 = and i8 %319, 3 + %321 = load i8, i8* %318, align 1 + %322 = and i8 %321, -4 + %323 = or i8 %322, %320 + store i8 %323, i8* %318, align 1 + %324 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %325 = load i8, i8* %324, align 1 + %326 = zext i8 %325 to i32 + %327 = lshr i32 %326, 1 + %328 = shl i32 %327, 29 + %329 = ashr i32 %328, 29 + %330 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 0 + %331 = load i8, i8* %330, align 1 + %332 = zext i8 %331 to i32 + %333 = lshr i32 %332, 4 + %334 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 1 + %335 = load i8, i8* %334, align 1 + %336 = zext i8 %335 to i32 + %337 = shl i32 %336, 4 + %338 = or i32 %337, %333 + %339 = shl i32 %338, 26 + %340 = ashr i32 %339, 26 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.31, i32 0, i32 0), i32 %329, i32 %340) + %341 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %342 = load i8, i8* %341, align 1 + %343 = lshr i8 %342, 5 + %344 = trunc i8 %343 to i1 + %boolsi = zext i1 %344 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.32, i32 0, i32 0), i32 %boolsi) + %345 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %346 = load i8, i8* %345, align 1 + %347 = and i8 %346, -33 + %348 = or i8 %347, 32 + store i8 %348, i8* %345, align 1 + %349 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %350 = load i8, i8* %349, align 1 + %351 = lshr i8 %350, 5 + %352 = trunc i8 %351 to i1 + %boolsi1 = zext i1 %352 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.33, i32 0, i32 0), i32 %boolsi1) + %353 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %354 = load i8, i8* %353, align 1 + %355 = and i8 %354, -33 + store i8 %355, i8* %353, align 1 + %356 = getelementptr inbounds [3 x i8], [3 x i8]* %xx, i64 0, i64 2 + %357 = load i8, i8* %356, align 1 + %358 = lshr i8 %357, 5 + %359 = trunc i8 %358 to i1 + %boolsi2 = zext i1 %359 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.34, i32 0, i32 0), i32 %boolsi2) + ret void +} diff --git a/test/test_suite/bitstruct/bitstruct_arrays_be.c3t b/test/test_suite/bitstruct/bitstruct_arrays_be.c3t new file mode 100644 index 000000000..d760a8a97 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_arrays_be.c3t @@ -0,0 +1,146 @@ +// #target: x64-darwin +module foo; + +module foo; + +bitstruct BitField3 : char[4] @bigendian +{ + uint c : 4..19; +} + +bitstruct BitField4 : char[4] +{ + uint c : 4..19; +} + +extern fn void printf(char*, ...); + +fn void main() +{ + test3(); +} +fn void test3() +{ + //BitField3 xx = { 0xdeadbeef }; + BitField3 xx = { 0xbeaf }; + printf("%x = beaf\n", xx.c); + BitField4 xy = { 0xbeaf }; + void *abc = &xy; + char[4]* z = abc; + printf("%x = beaf\n", xy.c); + xy.c = 0xbeef; + printf("%x = beef\n", xy.c); + xx.c = 0xbeef; + printf("%x = beef\n", xx.c); +} + + +/* #expect: foo.ll + +define void @foo.test3() #0 { +entry: + %xx = alloca [4 x i8], align 1 + %xy = alloca [4 x i8], align 1 + %abc = alloca i8*, align 8 + %z = alloca [4 x i8]*, align 8 + store [4 x i8] c"\E0\FB\0A\00", [4 x i8]* %xx, align 1 + %0 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 0 + %1 = load i8, i8* %0, align 1 + %2 = zext i8 %1 to i32 + %3 = lshr i32 %2, 4 + %4 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 1 + %5 = load i8, i8* %4, align 1 + %6 = zext i8 %5 to i32 + %7 = shl i32 %6, 4 + %8 = or i32 %7, %3 + %9 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 2 + %10 = load i8, i8* %9, align 1 + %11 = zext i8 %10 to i32 + %12 = shl i32 %11, 12 + %13 = or i32 %12, %8 + %14 = shl i32 %13, 16 + %15 = call i32 @llvm.bswap.i32(i32 %14) + %16 = and i32 65535, %15 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i32 0, i32 0), i32 %16) + store [4 x i8] c"\F0\EA\0B\00", [4 x i8]* %xy, align 1 + %ptrptr = bitcast [4 x i8]* %xy to i8* + store i8* %ptrptr, i8** %abc, align 8 + %17 = load i8*, i8** %abc, align 8 + %ptrptr1 = bitcast i8* %17 to [4 x i8]* + store [4 x i8]* %ptrptr1, [4 x i8]** %z, align 8 + %18 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 0 + %19 = load i8, i8* %18, align 1 + %20 = zext i8 %19 to i32 + %21 = lshr i32 %20, 4 + %22 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 1 + %23 = load i8, i8* %22, align 1 + %24 = zext i8 %23 to i32 + %25 = shl i32 %24, 4 + %26 = or i32 %25, %21 + %27 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 2 + %28 = load i8, i8* %27, align 1 + %29 = zext i8 %28 to i32 + %30 = shl i32 %29, 12 + %31 = or i32 %30, %26 + %32 = and i32 65535, %31 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.1, i32 0, i32 0), i32 %32) + %33 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 0 + %34 = load i8, i8* %33, align 1 + %35 = and i8 %34, 15 + %36 = or i8 %35, -16 + store i8 %36, i8* %33, align 1 + %37 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 1 + store i8 -18, i8* %37, align 1 + %38 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 2 + %39 = load i8, i8* %38, align 1 + %40 = and i8 %39, -16 + %41 = or i8 %40, 11 + store i8 %41, i8* %38, align 1 + %42 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 0 + %43 = load i8, i8* %42, align 1 + %44 = zext i8 %43 to i32 + %45 = lshr i32 %44, 4 + %46 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 1 + %47 = load i8, i8* %46, align 1 + %48 = zext i8 %47 to i32 + %49 = shl i32 %48, 4 + %50 = or i32 %49, %45 + %51 = getelementptr inbounds [4 x i8], [4 x i8]* %xy, i64 0, i64 2 + %52 = load i8, i8* %51, align 1 + %53 = zext i8 %52 to i32 + %54 = shl i32 %53, 12 + %55 = or i32 %54, %50 + %56 = and i32 65535, %55 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.2, i32 0, i32 0), i32 %56) + %57 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 0 + %58 = load i8, i8* %57, align 1 + %59 = and i8 %58, 15 + %60 = or i8 %59, -32 + store i8 %60, i8* %57, align 1 + %61 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 1 + store i8 -5, i8* %61, align 1 + %62 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 2 + %63 = load i8, i8* %62, align 1 + %64 = and i8 %63, -16 + %65 = or i8 %64, 14 + store i8 %65, i8* %62, align 1 + %66 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 0 + %67 = load i8, i8* %66, align 1 + %68 = zext i8 %67 to i32 + %69 = lshr i32 %68, 4 + %70 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 1 + %71 = load i8, i8* %70, align 1 + %72 = zext i8 %71 to i32 + %73 = shl i32 %72, 4 + %74 = or i32 %73, %69 + %75 = getelementptr inbounds [4 x i8], [4 x i8]* %xx, i64 0, i64 2 + %76 = load i8, i8* %75, align 1 + %77 = zext i8 %76 to i32 + %78 = shl i32 %77, 12 + %79 = or i32 %78, %74 + %80 = shl i32 %79, 16 + %81 = call i32 @llvm.bswap.i32(i32 %80) + %82 = and i32 65535, %81 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.3, i32 0, i32 0), i32 %82) + ret void +} \ No newline at end of file diff --git a/test/test_suite/bitstruct/bitstruct_general.c3 b/test/test_suite/bitstruct/bitstruct_general.c3 index eedaa5c7b..e1dbdc86a 100644 --- a/test/test_suite/bitstruct/bitstruct_general.c3 +++ b/test/test_suite/bitstruct/bitstruct_general.c3 @@ -1,21 +1,21 @@ -// #skip +// #target: x64-darwin module foo; bitstruct BitField -{ - int a : 3; - int b : 3; - int c : 2; +{// #error: followed by bitstruct type + int a : 0..2; + int b : 4..6; + int c : 7..8; } bitstruct BitField2 : char { - int a : 3; - int b : 3; - int c : 2; + int a : 0..2; + int b : 4..6; + int c : 7..8; } struct Packet @@ -31,17 +31,16 @@ struct Packet bitstruct BitField3 : char[3] { - int a : 3; - int b : 6; - int c : 10; - int d : 5; + int a : 0..2; + int b : 3..8; + int c : 9..18; + int d : 19..23; } -bitstruct BitField3 : char[3] @aligned +bitstruct BitField4 : char[3] @aligned { - int a : 3; - int b : 5; - int c : 8; - int d : 5; - void : 5; + int a : 0..2; + int b : 3..7; + int c : 8..15; + int d : 16..19; } diff --git a/test/test_suite/bitstruct/bitstruct_intcontainer.c3t b/test/test_suite/bitstruct/bitstruct_intcontainer.c3t new file mode 100644 index 000000000..ab72916f8 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_intcontainer.c3t @@ -0,0 +1,106 @@ +// #target: x64-darwin + +module foo; + +bitstruct BitFieldCross : uint +{ + uint d : 0..4; + int a : 5..22; + uint c : 23..31; +} + +bitstruct BitFieldCrossU : int +{ + uint d : 0..4; + uint a : 5..22; + uint c : 23..31; +} + +bitstruct BitFieldCrossUL : long +{ + uint d : 0..4; + uint a : 5..22; + uint c : 23..40; + uint e : 41..61; +} + +bitstruct BitFieldCrossULBE : long @bigendian +{ + uint d : 0..4; + uint a : 5..22; + uint c : 23..40; + uint e : 41..61; +} +extern fn void printf(char*, ...); + +fn void main() +{ + BitFieldCross xx = { 0, -177, 0 }; + printf("%d\n", xx.a); + xx = { 0xff, -177, 0xff }; + printf("%d\n", xx.a); + BitFieldCrossU xxu = { 0xff, 0x12345678, 0xffffffff }; + printf("%x\n", xxu.a); + BitFieldCrossUL xxy = { 0xff, 0x12345678, 0xefff_fffe, 0xfdca9597 }; + printf("%x, %x, %x\n", xxy.a, xxy.c, xxy.e); + BitFieldCrossULBE xxybe = { 0xff, 0x12345678, 0xefff_fffe, 0xfdca9597 }; + printf("%x, %x, %x\n", xxybe.a, xxybe.c, xxybe.e); +} + +/* #expect: foo.ll + +define void @main() #0 { +entry: + %xx = alloca i32, align 4 + %xxu = alloca i32, align 4 + %xxy = alloca i64, align 8 + %xxybe = alloca i64, align 8 + store i32 8382944, i32* %xx, align 4 + %0 = load i32, i32* %xx, align 4 + %1 = shl i32 %0, 9 + %2 = ashr i32 %1, 14 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %2) + store i32 2147478015, i32* %xx, align 4 + %3 = load i32, i32* %xx, align 4 + %4 = load i32, i32* %xx, align 4 + %5 = shl i32 %4, 9 + %6 = ashr i32 %5, 14 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %6) + store i32 -7680225, i32* %xxu, align 4 + %7 = load i32, i32* %xxu, align 4 + %8 = lshr i32 %7, 5 + %9 = and i32 %8, 524287 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i32 0, i32 0), i32 %9) + store i64 1525365675337109279, i64* %xxy, align 8 + %10 = load i64, i64* %xxy, align 8 + %11 = lshr i64 %10, 5 + %12 = and i64 %11, 524287 + %13 = trunc i64 %12 to i32 + %14 = load i64, i64* %xxy, align 8 + %15 = lshr i64 %14, 23 + %16 = and i64 %15, 524287 + %17 = trunc i64 %16 to i32 + %18 = load i64, i64* %xxy, align 8 + %19 = lshr i64 %18, 41 + %20 = and i64 %19, 4194303 + %21 = trunc i64 %20 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.3, i32 0, i32 0), i32 %13, i32 %17, i32 %21) + store i64 2292062829969091349, i64* %xxybe, align 8 + %22 = load i64, i64* %xxybe, align 8 + %23 = call i64 @llvm.bswap.i64(i64 %22) + %24 = lshr i64 %23, 5 + %25 = and i64 %24, 524287 + %26 = trunc i64 %25 to i32 + %27 = load i64, i64* %xxybe, align 8 + %28 = call i64 @llvm.bswap.i64(i64 %27) + %29 = lshr i64 %28, 23 + %30 = and i64 %29, 524287 + %31 = trunc i64 %30 to i32 + %32 = load i64, i64* %xxybe, align 8 + %33 = call i64 @llvm.bswap.i64(i64 %32) + %34 = lshr i64 %33, 41 + %35 = and i64 %34, 4194303 + %36 = trunc i64 %35 to i32 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.4, i32 0, i32 0), i32 %26, i32 %31, i32 %36) + ret void +} diff --git a/test/test_suite/bitstruct/bitstruct_overlap.c3 b/test/test_suite/bitstruct/bitstruct_overlap.c3 new file mode 100644 index 000000000..bbee3b194 --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_overlap.c3 @@ -0,0 +1,47 @@ +bitstruct Foo1 : char +{ + int a : 2..5; + int b : 5..6; // #error: Overlapping members +} + +bitstruct Foo2 : char +{ + int a : 2..5; + int b : 4..6; // #error: Overlapping members +} + +bitstruct Foo3 : char +{ + int a : 2..5; + int b : 2..6; // #error: Overlapping members +} + +bitstruct Foo4 : char +{ + int a : 2..5; + int b : 1..6; // #error: Overlapping members +} + +bitstruct Foo5 : char +{ + int a : 2..5; + int b : 1..3; // #error: Overlapping members +} + +bitstruct Foo6 : char +{ + int a : 2..5; + int b : 1..1; +} + +bitstruct Foo7 : char @overlap +{ + int a : 2..5; + int b : 1..3; +} + +bitstruct Foo8 : char +{ + int a : 2..5; + bool b : 3; // #error: Overlapping members +} diff --git a/test/test_suite/bitstruct/bitstruct_single_error.c3 b/test/test_suite/bitstruct/bitstruct_single_error.c3 new file mode 100644 index 000000000..663cf196c --- /dev/null +++ b/test/test_suite/bitstruct/bitstruct_single_error.c3 @@ -0,0 +1,19 @@ +bitstruct Foo1 : char +{ + char x : 1..1; +} + +bitstruct Foo2 : char +{ + char x : 1; // #error: Only booleans may use non-range indices +} + +bitstruct Foo3 : char +{ + bool x : 0..2; // #error: The bit width of 'bool' +} + +bitstruct Foo4 : int +{ + char x : 0..15; // #error: The bit width of 'char' +} \ No newline at end of file diff --git a/test/test_suite/bitstruct/missing_bitstruct_type.c3 b/test/test_suite/bitstruct/missing_bitstruct_type.c3 new file mode 100644 index 000000000..1b82e0c70 --- /dev/null +++ b/test/test_suite/bitstruct/missing_bitstruct_type.c3 @@ -0,0 +1,3 @@ +bitstruct BitField +{ // #error: followed by bitstruct +} \ No newline at end of file diff --git a/wrapper/src/wrapper.cpp b/wrapper/src/wrapper.cpp index 4c8ac2314..2470fb2cb 100644 --- a/wrapper/src/wrapper.cpp +++ b/wrapper/src/wrapper.cpp @@ -111,6 +111,13 @@ static bool llvm_link(ObjFormat format, const char **args, int arg_count, const extern "C" { + LLVMValueRef LLVMConstBswap(LLVMValueRef ConstantVal) + { + llvm::Constant *Val = llvm::unwrap(ConstantVal); + const llvm::APInt& i = Val->getUniqueInteger(); + return llvm::wrap(llvm::Constant::getIntegerValue(Val->getType(), i.byteSwap())); + } + LLVMValueRef LLVMConstGEP2(LLVMTypeRef Ty, LLVMValueRef ConstantVal, LLVMValueRef *ConstantIndices, unsigned NumIndices) { llvm::ArrayRef IdxList(llvm::unwrap(ConstantIndices, NumIndices),