diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index a4f552772..c604c8125 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -8,6 +8,10 @@ import std::math; const MAX_MEMORY_ALIGNMENT = 0x1000_0000; const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2; +macro bool @constant_is_power_of_2($x) @private +{ + return $x != 0 && ($x & ($x - 1)) == 0; +} /** * Load a vector from memory according to a mask assuming default alignment. @@ -37,7 +41,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru) * @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" * @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" * @require passthru.len == mask.len : "Mask and passthru must have the same length" - * @require math::is_power_of_2($alignment) : "The alignment must be a power of two" + * @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" * * @return "A vector with the loaded values where the mask is true, passthru where the mask is false" **/ @@ -80,7 +84,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru) * @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" * @require passthru.len == mask.len : "Mask and passthru must have the same length" * @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" - * @require math::is_power_of_2($alignment) : "The alignment must be a power of two" + * @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" * * @return "A vector with the loaded values where the mask is true, passthru where the mask is false" **/ @@ -115,7 +119,7 @@ macro masked_store(ptr, value, bool[<*>] mask) * @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match" * @require @typekind(value) == VECTOR : "Expected value to be a vector" * @require value.len == mask.len : "Mask and value must have the same length" - * @require math::is_power_of_2($alignment) : "The alignment must be a power of two" + * @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" * **/ macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment) @@ -150,13 +154,35 @@ macro scatter(ptrvec, value, bool[<*>] mask) * @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" * @require value.len == mask.len : "Mask and value must have the same length" * @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" - * @require math::is_power_of_2($alignment) : "The alignment must be a power of two" + * @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" **/ macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment) { return $$scatter(ptrvec, value, mask, $alignment); } +/** + * @param [in] x "the variable or dereferenced pointer to load." + * @param $alignment "the alignment to assume for the load" + * @return "returns the value of x" + * + * @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" + **/ +macro @unaligned_load(&x, usz $alignment) @builtin +{ + return $$unaligned_load(x, $alignment); +} + +/** + * @require $assignable(y, $typeof(*x)) : "The value doesn't match the variable" + * + * @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" + **/ +macro @unaligned_store(&x, y, usz $alignment) @builtin +{ + return $$unaligned_store(x, ($typeof(*x))y, $alignment); +} + macro @volatile_load(&x) @builtin { return $$volatile_load(x); diff --git a/releasenotes.md b/releasenotes.md index 301b0168d..fcdacfe9f 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -11,6 +11,7 @@ - Require `@export` functions to have `@export` types. - Disallow leading/trailing/duplicate '_' in module names. - Updated mangling. +- Added `$$unaligned_load` and `$$unaligned_store`. ### Fixes - Error with unsigned compare in `@ensure` when early returning 0 #1207. @@ -29,6 +30,7 @@ - Added `remove_first_item` `remove_last_item` and `remove_item` as aliases for the `match` functions. - Added @str_hash, @str_upper, @str_lower, @str_find compile time macros. - Remove "panic" text from unreachable() when safe mode is turned off. +- Added `@unaligned_store` and `@unaligned_load`. ## 0.6.0 Change list diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 47b358c8f..052878e90 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -965,6 +965,8 @@ typedef enum BUILTIN_SYSCLOCK, BUILTIN_TRAP, BUILTIN_TRUNC, + BUILTIN_UNALIGNED_LOAD, + BUILTIN_UNALIGNED_STORE, BUILTIN_UNREACHABLE, BUILTIN_VECCOMPLT, BUILTIN_VECCOMPLE, diff --git a/src/compiler/llvm_codegen_builtins.c b/src/compiler/llvm_codegen_builtins.c index f408fc65f..fdf2a70ee 100644 --- a/src/compiler/llvm_codegen_builtins.c +++ b/src/compiler/llvm_codegen_builtins.c @@ -161,6 +161,16 @@ INLINE void llvm_emit_atomic_store(GenContext *c, BEValue *result_value, Expr *e } } +INLINE void llvm_emit_unaligned_store(GenContext *c, BEValue *result_value, Expr *expr) +{ + BEValue value; + llvm_emit_expr(c, &value, expr->call_expr.arguments[0]); + llvm_emit_expr(c, result_value, expr->call_expr.arguments[1]); + llvm_value_deref(c, &value); + value.alignment = expr->call_expr.arguments[2]->const_expr.ixx.i.low; + llvm_store(c, &value, result_value); +} + INLINE void llvm_emit_atomic_fetch(GenContext *c, BuiltinFunction func, BEValue *result_value, Expr *expr) { BEValue value; @@ -232,6 +242,14 @@ INLINE void llvm_emit_atomic_load(GenContext *c, BEValue *result_value, Expr *ex LLVMSetOrdering(result_value->value, llvm_atomic_ordering(expr->call_expr.arguments[2]->const_expr.ixx.i.low)); } +INLINE void llvm_emit_unaligned_load(GenContext *c, BEValue *result_value, Expr *expr) +{ + llvm_emit_expr(c, result_value, expr->call_expr.arguments[0]); + llvm_value_deref(c, result_value); + result_value->alignment = expr->call_expr.arguments[1]->const_expr.ixx.i.low; + llvm_value_rvalue(c, result_value); +} + static inline LLVMValueRef llvm_syscall_asm(GenContext *c, LLVMTypeRef func_type, char *call) { return LLVMGetInlineAsm(func_type, call, strlen(call), @@ -837,6 +855,12 @@ void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) case BUILTIN_ATOMIC_FETCH_EXCHANGE: llvm_emit_atomic_fetch(c, func, result_value, expr); return; + case BUILTIN_UNALIGNED_LOAD: + llvm_emit_unaligned_load(c, result_value, expr); + return; + case BUILTIN_UNALIGNED_STORE: + llvm_emit_unaligned_store(c, result_value, expr); + return; case BUILTIN_ATOMIC_LOAD: llvm_emit_atomic_load(c, result_value, expr); return; diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index 26ff41d28..93a9d162b 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -825,6 +825,29 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) rtype = original->pointer; break; } + case BUILTIN_UNALIGNED_LOAD: + { + assert(arg_count == 2); + if (!sema_check_builtin_args(context, args, (BuiltinArg[]) {BA_POINTER, BA_INTEGER}, 2)) return false; + Type *original = type_flatten(args[0]->type); + if (original == type_voidptr) RETURN_SEMA_ERROR(args[0], "Expected a typed pointer."); + if (!sema_check_alignment_expression(context, args[1])) return false; + rtype = original->pointer; + break; + } + case BUILTIN_UNALIGNED_STORE: + { + assert(arg_count == 3); + if (!sema_check_builtin_args(context, args, (BuiltinArg[]) {BA_POINTER, BA_INTEGER }, 2)) return false; + Type *original = type_flatten(args[0]->type); + if (!sema_check_alignment_expression(context, args[2])) return false; + if (original != type_voidptr) + { + if (!cast_implicit(context, args[1], original->pointer)) return false; + } + rtype = args[1]->type; + break; + } case BUILTIN_VOLATILE_LOAD: { assert(arg_count == 1); @@ -1069,6 +1092,7 @@ static inline int builtin_expected_args(BuiltinFunction func) case BUILTIN_VECCOMPEQ: case BUILTIN_WASM_MEMORY_GROW: case BUILTIN_ANY_MAKE: + case BUILTIN_UNALIGNED_LOAD: return 2; case BUILTIN_EXPECT_WITH_PROBABILITY: case BUILTIN_FMA: @@ -1080,6 +1104,7 @@ static inline int builtin_expected_args(BuiltinFunction func) case BUILTIN_OVERFLOW_SUB: case BUILTIN_PREFETCH: case BUILTIN_ATOMIC_LOAD: + case BUILTIN_UNALIGNED_STORE: case BUILTIN_SELECT: return 3; case BUILTIN_ATOMIC_STORE: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index ed68d68f3..9c62fbf03 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -272,6 +272,8 @@ void symtab_init(uint32_t capacity) builtin_list[BUILTIN_VECCOMPEQ] = KW_DEF("veccompeq"); builtin_list[BUILTIN_VECCOMPNE] = KW_DEF("veccompne"); builtin_list[BUILTIN_UNREACHABLE] = KW_DEF("unreachable"); + builtin_list[BUILTIN_UNALIGNED_LOAD] = KW_DEF("unaligned_load"); + builtin_list[BUILTIN_UNALIGNED_STORE] = KW_DEF("unaligned_store"); builtin_list[BUILTIN_VOLATILE_LOAD] = KW_DEF("volatile_load"); builtin_list[BUILTIN_VOLATILE_STORE] = KW_DEF("volatile_store"); builtin_list[BUILTIN_WASM_MEMORY_GROW] = KW_DEF("wasm_memory_grow"); diff --git a/test/src/tester.py b/test/src/tester.py index 677b41799..0da805921 100644 --- a/test/src/tester.py +++ b/test/src/tester.py @@ -257,11 +257,10 @@ class Issues: if current_line >= len(lines): self.set_failed() print(file.filename + " did not contain: \"" + line + "\"") - print(""); - print("File dump: --------------------------------------------------->") + print("\n\n\n---------------------------------------------------> " + file.filename + "\n\n") print("\n".join(lines) + "\n") - print("<---------------------------------------------------- " + file.filename + " ends.") - print(""); + print("<---------------------------------------------------- " + self.sourcefile.filename) + print("") return if line in lines[current_line]: current_line += 1 diff --git a/test/test_suite/concurrency/atomic_load_store_debug.c3t b/test/test_suite/concurrency/atomic_load_store_debug.c3t index 33823eedd..8abc73c4d 100644 --- a/test/test_suite/concurrency/atomic_load_store_debug.c3t +++ b/test/test_suite/concurrency/atomic_load_store_debug.c3t @@ -115,7 +115,7 @@ declare i1 @llvm.expect.i1(i1, i1) !14 = !DILocalVariable(name: "x", scope: !6, file: !5, line: 13, type: !11, align: 4) !15 = !DILocation(line: 13, column: 6, scope: !6) !16 = !DILocation(line: -!17 = distinct !DISubprogram(name: "@atomic_load", linkageName: "@atomic_load", scope: !18, file: !18, +!17 = distinct !DISubprogram(name: "@atomic_load", !18 = !DIFile(filename: "mem.c3", !19 = !DILocation(line: 13, column: 10, scope: !6) !20 = !DILocalVariable(name: "y", scope: !6, file: !5, line: 14, type: !11, align: 4) @@ -125,13 +125,13 @@ declare i1 @llvm.expect.i1(i1, i1) !24 = !DILocation(line: 14, column: 10, scope: !6) !25 = !DILocation(line: 15, column: 25, scope: !6) !26 = !DILocation(line: 15, column: 19, scope: !6) -!27 = !DILocation(line: 212, column: 20, scope: !28, inlinedAt: !29) +!27 = !DILocation( !28 = distinct !DISubprogram(name: "@atomic_store", linkageName: "@atomic_store", scope: !18 !29 = !DILocation(line: 15, column: 2, scope: !6) !30 = !DILocation(line: 16, column: 24, scope: !6) !31 = !DILocation(line: 16, column: 19, scope: !6) -!32 = !DILocation(line: 212, column: 20, scope: !33, inlinedAt: !34) -!33 = distinct !DISubprogram(name: "@atomic_store", linkageName: "@atomic_store", scope: !18, file: !18, line: 210, scopeLine: 210, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !4 +!32 = !DILocation( +!33 = distinct !DISubprogram(name: "@atomic_store", linkageName: "@atomic_store", scope: !18 !34 = !DILocation(line: 16, column: 2, scope: !6) !35 = !DILocation(line: 17, column: 20, scope: !6) !36 = !DILocation(line: 17, column: 6, scope: !6) diff --git a/test/test_suite/debug_symbols/defer_macro.c3t b/test/test_suite/debug_symbols/defer_macro.c3t index adbe80645..20442c817 100644 --- a/test/test_suite/debug_symbols/defer_macro.c3t +++ b/test/test_suite/debug_symbols/defer_macro.c3t @@ -653,9 +653,8 @@ no_match: ; preds = %compare !69 = !DILocation(line: 33, column: 49, scope: !63) !70 = !DILocalVariable(name: "name", arg: 3, scope: !63, file: !5, line: 33, type: !37) !71 = !DILocation(line: 33, column: 63, scope: !63) -!72 = !DILocation(line: 564, column: 10, scope: !73, inlinedAt: !75) -!73 = distinct !DISubprogram(name: "new", linkageName: "new", scope: !74, file: !74, line: 561, scopeLine: 561, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition - +!72 = !DILocation(line: +!73 = distinct !DISubprogram(name: "new", linkageName: "new", scope: !74, file: !74 !75 = !DILocation(line: 34, column: 15, scope: !63) !76 = distinct !DISubprogram(name: "test", linkageName: "test.test", scope: !5, file: !5, line: 41, type: !77, scopeLine: 41, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition !77 = !DISubroutineType(types: !78) @@ -728,9 +727,10 @@ no_match: ; preds = %compare !147 = !DILocation(line: 24, column: 43, scope: !144, inlinedAt: !146) !148 = !DILocation(line: 226, column: 55, scope: !149, inlinedAt: !151) !151 = !DILocation(line: 216, column: 9, scope: !152, inlinedAt: !153) -!152 = distinct !DISubprogram(name: "alloc_array", linkageName: "alloc_array", scope: !150, file: !150, line: 214, scopeLine: 214, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition -!153 = !DILocation(line: 649, column: 20, scope: !154, inlinedAt: !155) -!154 = distinct !DISubprogram(name: "alloc_array", linkageName: "alloc_array", scope: !74, file: !74, line: 647, scopeLine: 647, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition +!152 = distinct !DISubprogram(name: "alloc_array", linkageName: "alloc_array", scope: !150, file: !150 +!153 = !DILocation(line: +!154 = distinct !DISubprogram(name: "alloc_array", linkageName: "alloc_array", scope: !74, file: !74 +!155 = !DILocation(line: !156 = !DILocation(line: 226, column: 40, scope: !149, inlinedAt: !151) !157 = !DILocation(line: 62, column: 7, scope: !158, inlinedAt: !159) !158 = distinct !DISubprogram(name: "malloc_try", linkageName: "malloc_try", scope: !150, file: !150, line: 60, scopeLine: 60, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition