memcopy! and memset! intrinsics
This commit is contained in:
+223
-2
@@ -2230,7 +2230,9 @@ old_intrinsic_spellings_are_not_recognized :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
bare_intrinsic_names_are_available_to_user_functions :: proc(t: ^testing.T) {
|
||||
text := `ptrcast func() i32 { return 1 }
|
||||
text := `memory :: import "./memory"
|
||||
|
||||
ptrcast func() i32 { return 1 }
|
||||
sizeof func() i32 { return 2 }
|
||||
alignof func() i32 { return 3 }
|
||||
minval func() i32 { return 4 }
|
||||
@@ -2241,19 +2243,29 @@ divexact func() i32 { return 8 }
|
||||
divceil func() i32 { return 9 }
|
||||
rem func() i32 { return 10 }
|
||||
mod func() i32 { return 11 }
|
||||
memcopy func() i32 { return 12 }
|
||||
memset func() i32 { return 13 }
|
||||
main func() i32 {
|
||||
return ptrcast() + sizeof() + alignof() + minval() + maxval() +
|
||||
divtrunc() + divfloor() + divexact() + divceil() + rem() + mod() - 66
|
||||
divtrunc() + divfloor() + divexact() + divceil() + rem() + mod() +
|
||||
memcopy() + memset() + memory.memcopy() + memory.memset() - 120
|
||||
}
|
||||
`
|
||||
directory := "/tmp/brolang-test-user-intrinsic-names"
|
||||
main_path := "/tmp/brolang-test-user-intrinsic-names/main.bro"
|
||||
memory_directory := "/tmp/brolang-test-user-intrinsic-names/memory"
|
||||
memory_path := "/tmp/brolang-test-user-intrinsic-names/memory/memory.bro"
|
||||
output := "/tmp/brolang-test-user-intrinsic-names-output"
|
||||
_ = os2.remove_all(directory)
|
||||
defer _ = os2.remove_all(directory)
|
||||
defer _ = os.remove(output)
|
||||
testing.expect(t, os.make_directory(directory) == nil)
|
||||
testing.expect(t, os.make_directory(memory_directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||
memory_text := `memcopy func() i32 { return 14 }
|
||||
memset func() i32 { return 15 }
|
||||
`
|
||||
testing.expect(t, os.write_entire_file(memory_path, transmute([]byte)memory_text))
|
||||
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||
}
|
||||
|
||||
@@ -2286,6 +2298,215 @@ intrinsic_call_diagnostics_are_precise :: proc(t: ^testing.T) {
|
||||
testing.expect(t, found_qualified)
|
||||
}
|
||||
|
||||
@(test)
|
||||
memory_intrinsics_compile_run_and_lower :: proc(t: ^testing.T) {
|
||||
text := `Pair :: struct { x i32, y i32 }
|
||||
|
||||
calls i32 = 0
|
||||
|
||||
destination_view func(value []mut u8) []mut u8 {
|
||||
calls += 1
|
||||
return value
|
||||
}
|
||||
|
||||
source_view func(value []u8) []u8 {
|
||||
calls += 1
|
||||
return value
|
||||
}
|
||||
|
||||
compile_value func() [4]mut u8 {
|
||||
source [4]mut u8 = [1, 2, 3, 4]
|
||||
destination [4]mut u8 = undefined
|
||||
memset!(&destination, undefined)
|
||||
memcopy!(&destination, &source)
|
||||
memset!(destination[1..3], 9)
|
||||
memcopy!(destination[..0], destination[..0])
|
||||
return destination
|
||||
}
|
||||
|
||||
known :: $compile_value()
|
||||
|
||||
main func() i32 {
|
||||
if known[0] != 1 or known[1] != 9 or known[2] != 9 or known[3] != 4 { return 1 }
|
||||
|
||||
bytes [4]mut u8 = undefined
|
||||
source [4]mut u8 = [4, 3, 2, 1]
|
||||
memset!(&bytes, undefined)
|
||||
memcopy!(destination_view(bytes[..]), source_view(source[..]))
|
||||
if calls != 2 or bytes[0] != 4 or bytes[3] != 1 { return 2 }
|
||||
memset!(bytes[1..3], 7)
|
||||
if bytes[1] != 7 or bytes[2] != 7 { return 3 }
|
||||
|
||||
wide [2]mut i32 = undefined
|
||||
memset!(&wide, 42)
|
||||
if wide[0] != 42 or wide[1] != 42 { return 4 }
|
||||
|
||||
pairs [2]mut Pair = undefined
|
||||
pair_source [2]mut Pair = [Pair{x = 1, y = 2}, Pair{x = 3, y = 4}]
|
||||
memcopy!(&pairs, &pair_source)
|
||||
memset!(pairs[1..], Pair{x = 7, y = 8})
|
||||
if pairs[0].x != 1 or pairs[1].y != 8 { return 5 }
|
||||
|
||||
memcopy!(bytes[..0], bytes[..0])
|
||||
return 0
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
ir_module := lower.lower(&hir_module)
|
||||
defer ir.destroy_module(&ir_module)
|
||||
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
||||
defer delete(llvm_text)
|
||||
|
||||
copy_count, set_count := 0, 0
|
||||
for function in ir_module.functions {
|
||||
for instruction in function.instructions {
|
||||
copy_count += 1 if instruction.op == .Mem_Copy else 0
|
||||
set_count += 1 if instruction.op == .Mem_Set else 0
|
||||
}
|
||||
}
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, copy_count >= 3)
|
||||
testing.expect(t, set_count >= 3)
|
||||
testing.expect(t, strings.contains(llvm_text, "memcopy_len_ok"))
|
||||
testing.expect(t, strings.contains(llvm_text, "memcopy_size_ok"))
|
||||
testing.expect(t, strings.contains(llvm_text, "memcopy_disjoint"))
|
||||
testing.expect(t, strings.contains(llvm_text, "call void @llvm.memcpy.p0.p0.i64"))
|
||||
testing.expect(t, strings.contains(llvm_text, "call void @llvm.memset.p0.i64"))
|
||||
testing.expect(t, strings.contains(llvm_text, "memset_loop"))
|
||||
|
||||
directory := "/tmp/brolang-test-memory-intrinsics"
|
||||
main_path := "/tmp/brolang-test-memory-intrinsics/main.bro"
|
||||
output := "/tmp/brolang-test-memory-intrinsics-output"
|
||||
_ = os2.remove_all(directory)
|
||||
defer _ = os2.remove_all(directory)
|
||||
defer _ = os.remove(output)
|
||||
testing.expect(t, os.make_directory(directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
memory_intrinsic_diagnostics_are_precise :: proc(t: ^testing.T) {
|
||||
text := `bad_length func() [1]mut u8 {
|
||||
destination [1]mut u8 = undefined
|
||||
source [2]mut u8 = [1, 2]
|
||||
memcopy!(&destination, &source)
|
||||
return destination
|
||||
}
|
||||
|
||||
bad_overlap func() [3]mut u8 {
|
||||
items [3]mut u8 = [1, 2, 3]
|
||||
memcopy!(items[..2], items[1..])
|
||||
return items
|
||||
}
|
||||
|
||||
bad_undefined func() u8 {
|
||||
items [1]mut u8 = [1]
|
||||
memset!(&items, undefined)
|
||||
return items[0]
|
||||
}
|
||||
|
||||
length_value :: $bad_length()
|
||||
overlap_value :: $bad_overlap()
|
||||
undefined_value :: $bad_undefined()
|
||||
|
||||
main func() void {
|
||||
immutable [2]u8 = [1, 2]
|
||||
mutable [2]mut u8 = undefined
|
||||
wide [2]mut u16 = undefined
|
||||
memcopy!(&immutable, &mutable)
|
||||
memcopy!(&mutable, &wide)
|
||||
memcopy!(1, 2)
|
||||
memset!(1, 0)
|
||||
memcopy!()
|
||||
memset!(&mutable)
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
expected := [?]string{
|
||||
"memcopy! source and destination lengths differ",
|
||||
"memcopy! source and destination overlap",
|
||||
"cannot read an undefined value at comptime",
|
||||
"memcopy! destination must be a mutable slice or mutable pointer-to-array",
|
||||
"memcopy! source and destination element types must match",
|
||||
"memset! destination must be a mutable slice or mutable pointer-to-array",
|
||||
"memcopy! expects 2 arguments, got 0",
|
||||
"memset! expects 2 arguments, got 1",
|
||||
}
|
||||
found: [len(expected)]bool
|
||||
for diagnostic in diagnostics.items {
|
||||
for message, index in expected {
|
||||
found[index] = found[index] || strings.contains(diagnostic.message, message)
|
||||
}
|
||||
}
|
||||
for present in found {
|
||||
testing.expect(t, present)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
memory_intrinsic_runtime_guards_trap :: proc(t: ^testing.T) {
|
||||
cases := [?]struct {
|
||||
directory, output, text: string,
|
||||
}{
|
||||
{
|
||||
"/tmp/brolang-test-memcopy-length-trap",
|
||||
"/tmp/brolang-test-memcopy-length-trap-output",
|
||||
`copy func(destination []mut u8, source []u8) void { memcopy!(destination, source) }
|
||||
main func() void {
|
||||
destination [2]mut u8 = undefined
|
||||
source [3]mut u8 = [1, 2, 3]
|
||||
copy(destination[..], source[..])
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
"/tmp/brolang-test-memcopy-overlap-trap",
|
||||
"/tmp/brolang-test-memcopy-overlap-trap-output",
|
||||
`copy func(destination []mut u8, source []u8) void { memcopy!(destination, source) }
|
||||
main func() void {
|
||||
items [4]mut u8 = [1, 2, 3, 4]
|
||||
copy(items[1..], items[..3])
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
for test_case in cases {
|
||||
main_path := fmt.tprintf("%s/main.bro", test_case.directory)
|
||||
_ = os2.remove_all(test_case.directory)
|
||||
defer _ = os2.remove_all(test_case.directory)
|
||||
defer _ = os.remove(test_case.output)
|
||||
testing.expect(t, os.make_directory(test_case.directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)test_case.text))
|
||||
testing.expect_value(t, compiler_core.compile_package(test_case.directory, test_case.output), 0)
|
||||
state := run_executable(test_case.output)
|
||||
testing.expect(t, !state.success)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
malformed_intrinsic_calls_have_targeted_parse_diagnostics :: proc(t: ^testing.T) {
|
||||
cases := [?]struct {
|
||||
|
||||
Reference in New Issue
Block a user