From cf59297a402d3a682c73ae9acd8ad78718bfd70c Mon Sep 17 00:00:00 2001 From: myuon Date: Mon, 18 Sep 2023 14:34:41 +0900 Subject: [PATCH 01/12] chore: dump the memory --- dump.sh | 19 +++++++++++++++++++ justfile | 2 +- src/runtime.rs | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 dump.sh diff --git a/dump.sh b/dump.sh new file mode 100644 index 00000000..168acb75 --- /dev/null +++ b/dump.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +input_file="./build/memory/memory.dat" +output_prefix="./build/memory/output_file" + +rm output_file* +split -d -b 250m "$input_file" "${output_prefix}_" + +chunk_number=0 +chunk_size=$((250 * 1024 * 1024)) # 250MB in bytes +file_size=$(stat -f%z "$input_file") +offset=0 + +while [ $offset -lt $file_size ]; do + chunk_number_hex=$(printf '%02d' $chunk_number) + hexdump -C -s $offset "${output_prefix}_${chunk_number_hex}" > "${output_prefix}_${chunk_number_hex}.hexdump" + offset=$(($offset + $chunk_size)) + chunk_number=$(($chunk_number + 1)) +done diff --git a/justfile b/justfile index a32d58b9..ba323a12 100644 --- a/justfile +++ b/justfile @@ -33,7 +33,7 @@ build_current_compiler: run file options="": @just build_current_compiler MODE=run-wat WAT_FILE=./build/quartz-current.wat cargo run --release -- compile {{options}} -o ./build/quartz-compiled.wat {{file}} - MODE=run-wat WAT_FILE=./build/quartz-compiled.wat cargo run --release + MEMORY_DUMP_FILE=./build/memory/memory.dat MODE=run-wat WAT_FILE=./build/quartz-compiled.wat cargo run --release test file options="": @just build_current_compiler diff --git a/src/runtime.rs b/src/runtime.rs index 739d973c..9d8ff335 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,7 +1,9 @@ use std::io::Write; use anyhow::{anyhow, Result}; -use wasmer::{imports, Function, Instance, Module, Store, Value as WasmValue}; +use wasmer::{ + imports, Function, Instance, MemoryAccessError, MemoryView, Module, Store, Value as WasmValue, +}; use wasmer_wasi::WasiState; use crate::value::Value; @@ -35,7 +37,10 @@ impl Runtime { "debug" => Function::new_typed(&mut store, |i: i64| { let w = Value::from_i64(i); - println!("[DEBUG] {:?} ({:#032b} | {:#b})", w, (i >> 32) as i32, i & 0xffffffff); + println!("[DEBUG] {} ({:#032b} | {:#b})", match w { + Value::Pointer(p) => format!("Pointer(0x{:x})", p), + _ => format!("{:?}", w), + }, (i >> 32) as i32, i & 0xffffffff); Value::i32(0).as_i64() }), "abort" => Function::new_typed(&mut store, || -> i64 { @@ -66,9 +71,33 @@ impl Runtime { let main = instance.exports.get_function("main")?; let result = main.call(&mut store, &[])?; + if let Ok(file_path) = std::env::var("MEMORY_DUMP_FILE") { + let memory = instance.exports.get_memory("memory")?; + let memory = memory.view(&mut store); + + let mut file = std::fs::File::create(file_path).unwrap(); + file.write_all(&Runtime::copy_to_vec(&memory).unwrap()) + .unwrap(); + } + Ok(result) } + // Use MemoryView::copy_to_vec + fn copy_to_vec(view: &MemoryView) -> Result, MemoryAccessError> { + let mut new_memory = Vec::new(); + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < view.data_size() { + let remaining = view.data_size() - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + view.read(offset, &mut chunk[..sublen])?; + new_memory.extend_from_slice(&chunk[..sublen]); + offset += sublen as u64; + } + Ok(new_memory) + } + pub fn run(&mut self, input: &str) -> Result> { self._run(input).map_err(|err| { let message = err.to_string(); From 56f9152b1acd8729efff2eae246a4c01597dff4a Mon Sep 17 00:00:00 2001 From: myuon Date: Mon, 18 Sep 2023 16:57:15 +0900 Subject: [PATCH 02/12] feat: generate memorydump --- dump.sh | 19 ---- justfile | 2 +- memorydump/go.mod | 3 + memorydump/main.go | 182 ++++++++++++++++++++++++++++++++++ memorydump/memorydump_test.go | 7 ++ src/runtime.rs | 35 +++---- 6 files changed, 207 insertions(+), 41 deletions(-) delete mode 100644 dump.sh create mode 100644 memorydump/go.mod create mode 100644 memorydump/main.go create mode 100644 memorydump/memorydump_test.go diff --git a/dump.sh b/dump.sh deleted file mode 100644 index 168acb75..00000000 --- a/dump.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -input_file="./build/memory/memory.dat" -output_prefix="./build/memory/output_file" - -rm output_file* -split -d -b 250m "$input_file" "${output_prefix}_" - -chunk_number=0 -chunk_size=$((250 * 1024 * 1024)) # 250MB in bytes -file_size=$(stat -f%z "$input_file") -offset=0 - -while [ $offset -lt $file_size ]; do - chunk_number_hex=$(printf '%02d' $chunk_number) - hexdump -C -s $offset "${output_prefix}_${chunk_number_hex}" > "${output_prefix}_${chunk_number_hex}.hexdump" - offset=$(($offset + $chunk_size)) - chunk_number=$(($chunk_number + 1)) -done diff --git a/justfile b/justfile index ba323a12..fb296d2e 100644 --- a/justfile +++ b/justfile @@ -33,7 +33,7 @@ build_current_compiler: run file options="": @just build_current_compiler MODE=run-wat WAT_FILE=./build/quartz-current.wat cargo run --release -- compile {{options}} -o ./build/quartz-compiled.wat {{file}} - MEMORY_DUMP_FILE=./build/memory/memory.dat MODE=run-wat WAT_FILE=./build/quartz-compiled.wat cargo run --release + MEMORY_DUMP_FILE=./build/memory/memory.bin MODE=run-wat WAT_FILE=./build/quartz-compiled.wat cargo run --release test file options="": @just build_current_compiler diff --git a/memorydump/go.mod b/memorydump/go.mod new file mode 100644 index 00000000..c681c9d2 --- /dev/null +++ b/memorydump/go.mod @@ -0,0 +1,3 @@ +module github.com/myuon/quartz/memorydump + +go 1.21.1 diff --git a/memorydump/main.go b/memorydump/main.go new file mode 100644 index 00000000..391bc027 --- /dev/null +++ b/memorydump/main.go @@ -0,0 +1,182 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +func appendByteAsHex(bs []byte, b byte) []byte { + if b < 16 { + bs = append(bs, '0') + } else { + h := b >> 4 + if h < 10 { + bs = append(bs, '0'+h) + } else { + bs = append(bs, 'a'+h-10) + } + } + + h2 := b & 0x0f + if h2 < 10 { + bs = append(bs, '0'+h2) + } else { + bs = append(bs, 'a'+h2-10) + } + + return bs +} + +func hexdump(data []byte, offset int, writer io.Writer) { + skip := false + + dataOffset := 0 + unitSize := 16 * 1000 + for dataOffset < len(data) { + unit := data[dataOffset:min(dataOffset+unitSize, len(data))] + + for i := 0; i < len(unit)/16; i += 1 { + addr := i * 16 + chunk := unit[addr:min(addr+16, len(unit))] + hexVals := make([]byte, 0, 48) + asciiVals := make([]byte, 0, 16) + nonZeroFlag := false + for _, b := range chunk { + // if b < 16 { + // hexVals = append(hexVals, '0') + // } + // hexVals = strconv.AppendUint(hexVals, uint64(b), 16) + hexVals = appendByteAsHex(hexVals, b) + hexVals = append(hexVals, ' ') + + if 32 <= b && b <= 126 { + asciiVals = append(asciiVals, b) + } else { + asciiVals = append(asciiVals, '.') + } + + if b != 0 { + nonZeroFlag = true + } + } + + if nonZeroFlag { + fmt.Fprintf(writer, "%08x %s |%s|\n", addr+offset, string(hexVals), string(asciiVals)) + skip = false + } else { + if !skip { + fmt.Fprintf(writer, "%08x *\n", addr+offset) + + skip = true + } + } + } + + dataOffset += unitSize + } +} + +func deletePreviousFiles(pattern string) { + files, err := filepath.Glob(pattern) + if err != nil { + panic(err) + } + + for _, f := range files { + err := os.Remove(f) + if err != nil { + panic(err) + } + fmt.Printf("Deleted previous file: %s\n", f) + } +} + +func main() { + // ファイルパスとチャンクサイズを指定 + filePath := "./build/memory/memory.bin" + chunkSize := 250 * 1024 * 1024 + ext := filepath.Ext(filePath) + baseName := strings.Replace(filepath.Base(filePath), ext, "", 1) + dirPath := filepath.Dir(filePath) + + // 前回作成したファイルを削除 + deletePreviousFiles(filepath.Join(dirPath, fmt.Sprintf("%s_chunk_*", baseName))) + + // ファイルのサイズを取得 + fileInfo, err := os.Stat(filePath) + if err != nil { + panic(err) + } + fileSize := fileInfo.Size() + + // ファイルを開く + file, err := os.Open(filePath) + if err != nil { + panic(err) + } + defer file.Close() + + chunkFilePaths := []string{} + + // チャンクごとに処理 + for i := int64(0); i < fileSize; i += int64(chunkSize) { + fmt.Printf("Processing chunk starting at byte: %d\n", i) + + chunkData := make([]byte, chunkSize) + n, err := file.Read(chunkData) + if err != nil && err != io.EOF { + panic(err) + } + chunkData = chunkData[:n] + + chunkFilePath := filepath.Join(dirPath, fmt.Sprintf("%s_chunk_%d%s", baseName, i/int64(chunkSize), ext)) + chunkFilePaths = append(chunkFilePaths, chunkFilePath) + + chunkFile, err := os.Create(chunkFilePath) + if err != nil { + panic(err) + } + + _, err = chunkFile.Write(chunkData) + if err != nil { + panic(err) + } + + chunkFile.Close() + fmt.Printf("Created chunk file: %s\n", chunkFilePath) + } + + for index, chunkFilePath := range chunkFilePaths { + fmt.Printf("Generating hexdump for file: %s\n", chunkFilePath) + + chunkFile, err := os.Open(chunkFilePath) + if err != nil { + panic(err) + } + defer chunkFile.Close() + + chunkData := make([]byte, chunkSize) + n, err := chunkFile.Read(chunkData) + if err != nil && err != io.EOF { + panic(err) + } + chunkData = chunkData[:n] + + hexdumpFilePath := fmt.Sprintf("%v.hexdump", chunkFilePath) + hexdumpFile, err := os.Create(hexdumpFilePath) + if err != nil { + panic(err) + } + writer := bufio.NewWriter(hexdumpFile) + + hexdump(chunkData, index*chunkSize, writer) + writer.Flush() + hexdumpFile.Close() + + fmt.Printf("Created hexdump file: %s\n", hexdumpFilePath) + } +} diff --git a/memorydump/memorydump_test.go b/memorydump/memorydump_test.go new file mode 100644 index 00000000..4f559228 --- /dev/null +++ b/memorydump/memorydump_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func Test_prof(t *testing.T) { + main() +} diff --git a/src/runtime.rs b/src/runtime.rs index 9d8ff335..04cb0966 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,9 +1,7 @@ use std::io::Write; use anyhow::{anyhow, Result}; -use wasmer::{ - imports, Function, Instance, MemoryAccessError, MemoryView, Module, Store, Value as WasmValue, -}; +use wasmer::{imports, Function, Instance, Module, Store, Value as WasmValue}; use wasmer_wasi::WasiState; use crate::value::Value; @@ -73,29 +71,24 @@ impl Runtime { if let Ok(file_path) = std::env::var("MEMORY_DUMP_FILE") { let memory = instance.exports.get_memory("memory")?; - let memory = memory.view(&mut store); + let view = memory.view(&mut store); let mut file = std::fs::File::create(file_path).unwrap(); - file.write_all(&Runtime::copy_to_vec(&memory).unwrap()) - .unwrap(); - } - Ok(result) - } + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < view.data_size() { + let remaining = view.data_size() - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + view.read(offset, &mut chunk[..sublen])?; + + file.write_all(&chunk[..sublen]).unwrap(); - // Use MemoryView::copy_to_vec - fn copy_to_vec(view: &MemoryView) -> Result, MemoryAccessError> { - let mut new_memory = Vec::new(); - let mut offset = 0; - let mut chunk = [0u8; 40960]; - while offset < view.data_size() { - let remaining = view.data_size() - offset; - let sublen = remaining.min(chunk.len() as u64) as usize; - view.read(offset, &mut chunk[..sublen])?; - new_memory.extend_from_slice(&chunk[..sublen]); - offset += sublen as u64; + offset += sublen as u64; + } } - Ok(new_memory) + + Ok(result) } pub fn run(&mut self, input: &str) -> Result> { From 425fc9750fa918f620029d320629afff134568db Mon Sep 17 00:00:00 2001 From: myuon Date: Mon, 18 Sep 2023 17:03:49 +0900 Subject: [PATCH 03/12] chore: goroutines for memorydump --- memorydump/main.go | 57 +++++++++++++++++++++----------------- quartz/std.qz | 69 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 79 insertions(+), 47 deletions(-) diff --git a/memorydump/main.go b/memorydump/main.go index 391bc027..4b478e27 100644 --- a/memorydump/main.go +++ b/memorydump/main.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "strings" + "sync" ) func appendByteAsHex(bs []byte, b byte) []byte { @@ -46,10 +47,6 @@ func hexdump(data []byte, offset int, writer io.Writer) { asciiVals := make([]byte, 0, 16) nonZeroFlag := false for _, b := range chunk { - // if b < 16 { - // hexVals = append(hexVals, '0') - // } - // hexVals = strconv.AppendUint(hexVals, uint64(b), 16) hexVals = appendByteAsHex(hexVals, b) hexVals = append(hexVals, ' ') @@ -150,33 +147,41 @@ func main() { fmt.Printf("Created chunk file: %s\n", chunkFilePath) } + wg := sync.WaitGroup{} for index, chunkFilePath := range chunkFilePaths { - fmt.Printf("Generating hexdump for file: %s\n", chunkFilePath) + wg.Add(1) + go func(index int, chunkFilePath string) { + defer wg.Done() - chunkFile, err := os.Open(chunkFilePath) - if err != nil { - panic(err) - } - defer chunkFile.Close() + fmt.Printf("Generating hexdump for file: %s\n", chunkFilePath) - chunkData := make([]byte, chunkSize) - n, err := chunkFile.Read(chunkData) - if err != nil && err != io.EOF { - panic(err) - } - chunkData = chunkData[:n] + chunkFile, err := os.Open(chunkFilePath) + if err != nil { + panic(err) + } + defer chunkFile.Close() - hexdumpFilePath := fmt.Sprintf("%v.hexdump", chunkFilePath) - hexdumpFile, err := os.Create(hexdumpFilePath) - if err != nil { - panic(err) - } - writer := bufio.NewWriter(hexdumpFile) + chunkData := make([]byte, chunkSize) + n, err := chunkFile.Read(chunkData) + if err != nil && err != io.EOF { + panic(err) + } + chunkData = chunkData[:n] + + hexdumpFilePath := fmt.Sprintf("%v.hexdump", chunkFilePath) + hexdumpFile, err := os.Create(hexdumpFilePath) + if err != nil { + panic(err) + } + writer := bufio.NewWriter(hexdumpFile) - hexdump(chunkData, index*chunkSize, writer) - writer.Flush() - hexdumpFile.Close() + hexdump(chunkData, index*chunkSize, writer) + writer.Flush() + hexdumpFile.Close() - fmt.Printf("Created hexdump file: %s\n", hexdumpFilePath) + fmt.Printf("Created hexdump file: %s\n", hexdumpFilePath) + }(index, chunkFilePath) } + + wg.Wait() } diff --git a/quartz/std.qz b/quartz/std.qz index e32c2d38..c7e9fd63 100644 --- a/quartz/std.qz +++ b/quartz/std.qz @@ -352,14 +352,14 @@ module i32 { @[test] fun test_i32_to_string() { assert_eq(1234.to_string(), "1234"); - assert_eq((0-1234).to_string(), "-1234"); + assert_eq((0 - 1234).to_string(), "-1234"); assert_eq(0.to_string(), "0"); } @[test] fun test_i32_parse() { assert_eq(i32::parse("1234"), 1234); - assert_eq(i32::parse("-1234"), 0-1234); + assert_eq(i32::parse("-1234"), 0 - 1234); assert_eq(i32::parse("0"), 0); } @@ -481,13 +481,19 @@ module i64 { } while n.hi != 0 || n.lo != 0 { - let r = n.mod(i64 { hi: 0, lo: 10 }); + let r = n.mod(i64 { + hi: 0, + lo: 10, + }); if r.hi != 0 { panic("i64.to_string: r.hi != 0"); } result.push((r.lo + 48) as byte); - n = n.div(i64 { hi: 0, lo: 10 }); + n = n.div(i64 { + hi: 0, + lo: 10, + }); } return vec_byte_to_string(result).reverse(); @@ -828,10 +834,7 @@ fun test_split() { assert_eq("abc".split("abc"), make[vec[string]]("")); assert_eq("abc".split("def"), make[vec[string]]("abc")); assert_eq("abcbdbebfbg".split("b"), make[vec[string]]("a", "c", "d", "e", "f", "g")); - assert_eq( - "apple/banana/orange/lime".split("/"), - make[vec[string]]("apple", "banana", "orange", "lime"), - ); + assert_eq("apple/banana/orange/lime".split("/"), make[vec[string]]("apple", "banana", "orange", "lime")); } @[test] @@ -1262,7 +1265,7 @@ fun is_a_record(p: ptr[ptr[any]]): bool { fun test_is_a_record() { assert(!is_a_record(nil as ptr[ptr[any]])); assert(!is_a_record(10 as ptr[ptr[any]])); - assert(is_a_record(make[vec[i32]](1,2,3) as ptr[ptr[any]])); + assert(is_a_record(make[vec[i32]](1, 2, 3) as ptr[ptr[any]])); assert(is_a_record("string" as ptr[ptr[any]])); } @@ -1457,7 +1460,11 @@ module derive { if k > 0 { s = s.concat(", "); } - s = s.concat(format("{}:{}", derive::to_string_internal(key, pretty, depth + 1), derive::to_string_internal(value, pretty, depth + 1))); + s = s.concat(format( + "{}:{}", + derive::to_string_internal(key, pretty, depth + 1), + derive::to_string_internal(value, pretty, depth + 1), + )); } return s.concat(")"); @@ -1504,7 +1511,10 @@ fun test_derive_to_string() { assert_eq(derive::to_string(1), "1"); assert_eq(derive::to_string(true), "true"); assert_eq(derive::to_string("abc"), "\"abc\""); - assert_eq(derive::to_string(make[vec[string]]("a", "b", "cdef\nghi")), "vec(\"a\", \"b\", \"cdef\nghi\")"); + assert_eq( + derive::to_string(make[vec[string]]("a", "b", "cdef\nghi")), + "vec(\"a\", \"b\", \"cdef\nghi\")", + ); assert_eq( derive::to_string(struct { name: "foo", @@ -1567,9 +1577,13 @@ enum WasiError { module WasiError { fun from_errno(code: i32): WasiError { if code == 44 { - return WasiError { no_entry: true }; + return WasiError { + no_entry: true, + }; } else { - return WasiError { unknown: code }; + return WasiError { + unknown: code, + }; } } } @@ -1718,11 +1732,13 @@ fun mark(value: any) { header.set_is_marked(true); + debug(header); + let rep = (header.get_type_rep() as ptr[TypeRep]).at(0); // FIXME: Why is this necessary? - if !types::is_pointer(rep) { - return; - } + // if !types::is_pointer(rep) { + // return; + // } if rep.nilptr != nil { debug(error_invalid_type); debug(rep.nilptr); @@ -1780,7 +1796,7 @@ fun test_types_is_i32() { assert(types::is_i32(1)); assert(!types::is_i32(true)); assert(!types::is_i32("abc")); - assert(!types::is_i32(make[vec[i32]](1,2,3))); + assert(!types::is_i32(make[vec[i32]](1, 2, 3))); } @[test] @@ -1788,7 +1804,7 @@ fun test_types_is_pointer() { assert(!types::is_pointer(1)); assert(!types::is_pointer(true)); assert(types::is_pointer("abc")); - assert(types::is_pointer(make[vec[i32]](1,2,3))); + assert(types::is_pointer(make[vec[i32]](1, 2, 3))); } @[test] @@ -1796,7 +1812,7 @@ fun test_types_is_bool() { assert(!types::is_bool(1)); assert(types::is_bool(true)); assert(!types::is_bool("abc")); - assert(!types::is_bool(make[vec[i32]](1,2,3))); + assert(!types::is_bool(make[vec[i32]](1, 2, 3))); } @[test] @@ -1845,7 +1861,12 @@ fun environs(): map[string, string] { let mp = make[map[string, string]](); for i in 0..count_i32 { - let p = i32_from_bytes(buf_index_ptrbyte.at(i * 4 + 0), buf_index_ptrbyte.at(i * 4 + 1), buf_index_ptrbyte.at(i * 4 + 2), buf_index_ptrbyte.at(i * 4 + 3)) as ptr[byte]; + let p = i32_from_bytes( + buf_index_ptrbyte.at(i * 4 + 0), + buf_index_ptrbyte.at(i * 4 + 1), + buf_index_ptrbyte.at(i * 4 + 2), + buf_index_ptrbyte.at(i * 4 + 3), + ) as ptr[byte]; let kv = read_until_null(p).split("="); if kv.length == 1 { mp.insert(kv.at(0), ""); @@ -1882,7 +1903,12 @@ fun args(): vec[string] { let args = make[vec[string]](); for i in 0..count_i32 { - let p = i32_from_bytes(buf_index_ptrbyte.at(i * 4 + 0), buf_index_ptrbyte.at(i * 4 + 1), buf_index_ptrbyte.at(i * 4 + 2), buf_index_ptrbyte.at(i * 4 + 3)) as ptr[byte]; + let p = i32_from_bytes( + buf_index_ptrbyte.at(i * 4 + 0), + buf_index_ptrbyte.at(i * 4 + 1), + buf_index_ptrbyte.at(i * 4 + 2), + buf_index_ptrbyte.at(i * 4 + 3), + ) as ptr[byte]; let arg = read_until_null(p); args.push(arg); } @@ -1907,3 +1933,4 @@ fun assert(a: bool) { panic("assert failed"); } } + From adaf02ae97080d86ec70d7f9e3307154cec80910 Mon Sep 17 00:00:00 2001 From: myuon Date: Mon, 18 Sep 2023 17:12:55 +0900 Subject: [PATCH 04/12] chore: specify 00 start and end --- memorydump/main.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/memorydump/main.go b/memorydump/main.go index 4b478e27..5530fb8e 100644 --- a/memorydump/main.go +++ b/memorydump/main.go @@ -62,11 +62,16 @@ func hexdump(data []byte, offset int, writer io.Writer) { } if nonZeroFlag { + if skip { + fmt.Fprintf(writer, "%08x 00\n", addr+offset) + } + fmt.Fprintf(writer, "%08x %s |%s|\n", addr+offset, string(hexVals), string(asciiVals)) skip = false } else { if !skip { - fmt.Fprintf(writer, "%08x *\n", addr+offset) + fmt.Fprintf(writer, "%08x 00\n", addr+offset) + fmt.Fprint(writer, "...\n") skip = true } @@ -75,6 +80,10 @@ func hexdump(data []byte, offset int, writer io.Writer) { dataOffset += unitSize } + + if skip { + fmt.Fprintf(writer, "%08x 00\n", dataOffset+offset) + } } func deletePreviousFiles(pattern string) { From fa75b266f7f2ec95477518b655de1c7b7fe925fe Mon Sep 17 00:00:00 2001 From: myuon Date: Mon, 18 Sep 2023 18:34:12 +0900 Subject: [PATCH 05/12] fix: generate u32 numbers --- quartz/generator.qz | 9 ++++++--- quartz/std.qz | 5 +++++ quartz/value.qz | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/quartz/generator.qz b/quartz/generator.qz index d2e8c708..ac289be3 100644 --- a/quartz/generator.qz +++ b/quartz/generator.qz @@ -739,9 +739,10 @@ module Generator { self.write(format(" ;; {}", expr.t_u32!.to_string())); } - // FIXME: i64::to_string is broken! self.new_statement(); - self.write(format("{}.const {}", Value::wasm_type(), expr.t_u32!.to_string())); + self.write_value(Value { + t_u32: expr.t_u32!, + }); } else if expr.t_nil != nil { if MODE_READABLE_WASM { self.new_statement(); @@ -758,7 +759,9 @@ module Generator { // push to stack let is_core_function = self.current_function_name.starts_with("quartz_core"); - if (expr.t_let!.type_.t_address != nil || expr.t_let!.type_.t_any != nil) && !self.globals.has(expr.t_let!.name) && !is_core_function { + if (expr.t_let!.type_.t_address != nil || expr.t_let!.type_.t_any != nil) && !self.globals.has( + expr.t_let!.name, + ) && !is_core_function { self.new_statement(); self.expression(IrTerm { t_ident: expr.t_let!.name, diff --git a/quartz/std.qz b/quartz/std.qz index c7e9fd63..c9b416c9 100644 --- a/quartz/std.qz +++ b/quartz/std.qz @@ -506,6 +506,11 @@ fun test_i64_to_string() { assert_eq((1 as i64).to_string(), "1"); } +@[test] +fun test_i64_shift() { + assert_eq(((2883584000 as i64) << (32 as i64)).to_string(), "12384898975268864000"); +} + struct string { data: ptr[byte], length: i32, diff --git a/quartz/value.qz b/quartz/value.qz index a30826e9..a872ddab 100644 --- a/quartz/value.qz +++ b/quartz/value.qz @@ -26,7 +26,7 @@ module Value { } else if self.t_i32 != nil { return (self.t_i32! as i64) << (32 as i64); } else if self.t_u32 != nil { - return (self.t_u32! << (32 as u32)) as i64; + return (self.t_u32! as i64) << (32 as i64); } else if self.t_pointer != nil { // return (self.t_pointer! as i64) << (32 as i64) | (0b1 as i64); return (self.t_pointer! as i64) << (32 as i64) | (1 as i64); From 863d7b6a2b6333a0cdefbaf5e0585f1dfac11cbe Mon Sep 17 00:00:00 2001 From: myuon Date: Mon, 18 Sep 2023 18:34:31 +0900 Subject: [PATCH 06/12] feat: dump memory when aborted --- src/runtime.rs | 71 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/src/runtime.rs b/src/runtime.rs index 04cb0966..a976d37c 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,11 +1,22 @@ use std::io::Write; use anyhow::{anyhow, Result}; -use wasmer::{imports, Function, Instance, Module, Store, Value as WasmValue}; +use wasmer::{imports, Function, Instance, MemoryView, Module, Store, Value as WasmValue}; use wasmer_wasi::WasiState; use crate::value::Value; +#[derive(Debug, Clone, Copy)] +struct ExitCode(u32); + +impl std::fmt::Display for ExitCode { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for ExitCode {} + pub struct Runtime {} impl Runtime { @@ -13,6 +24,28 @@ impl Runtime { Runtime {} } + fn dump_memory(view: MemoryView) -> Result<()> { + if let Ok(file_path) = std::env::var("MEMORY_DUMP_FILE") { + let mut file = std::fs::File::create(file_path.clone()).unwrap(); + + let mut offset = 0; + let mut chunk = [0u8; 40960]; + while offset < view.data_size() { + let remaining = view.data_size() - offset; + let sublen = remaining.min(chunk.len() as u64) as usize; + view.read(offset, &mut chunk[..sublen])?; + + file.write_all(&chunk[..sublen]).unwrap(); + + offset += sublen as u64; + } + + println!("Memory dumped to file: {}", file_path); + } + + Ok(()) + } + pub fn _run(&mut self, wat: &str) -> Result> { let mut store = Store::default(); let module = Module::new(&store, &wat)?; @@ -30,6 +63,12 @@ impl Runtime { .finalize(&mut store)?; let wasi_import_object = wasi_func_env.import_object(&mut store, &module)?; + fn abort() -> Result { + println!("[ABORT]"); + + Err(ExitCode(1)) + } + let mut import_object = imports! { "env" => { "debug" => Function::new_typed(&mut store, |i: i64| { @@ -41,9 +80,7 @@ impl Runtime { }, (i >> 32) as i32, i & 0xffffffff); Value::i32(0).as_i64() }), - "abort" => Function::new_typed(&mut store, || -> i64 { - panic!("[ABORT]"); - }), + "abort" => Function::new_typed(&mut store, abort), // @Deprecated: will be removed in 2.3.0+ "i64_to_string_at" => Function::new_typed(&mut store, |a_value: i64, b_value: i64, at_value: i64| { let a = Value::from_i64(a_value).as_i32().unwrap(); @@ -67,28 +104,16 @@ impl Runtime { wasi_func_env.initialize(&mut store, &instance)?; let main = instance.exports.get_function("main")?; - let result = main.call(&mut store, &[])?; - - if let Ok(file_path) = std::env::var("MEMORY_DUMP_FILE") { - let memory = instance.exports.get_memory("memory")?; - let view = memory.view(&mut store); - - let mut file = std::fs::File::create(file_path).unwrap(); - - let mut offset = 0; - let mut chunk = [0u8; 40960]; - while offset < view.data_size() { - let remaining = view.data_size() - offset; - let sublen = remaining.min(chunk.len() as u64) as usize; - view.read(offset, &mut chunk[..sublen])?; + let result = main + .call(&mut store, &[]) + .map_err(|err| anyhow!("calling main: {:?}", err)); - file.write_all(&chunk[..sublen]).unwrap(); + let memory = instance.exports.get_memory("memory")?; + let view = memory.view(&mut store); - offset += sublen as u64; - } - } + Runtime::dump_memory(view)?; - Ok(result) + result } pub fn run(&mut self, input: &str) -> Result> { From 8edc27e2c3bac38d2eaf56f84ee0bf763f9620c3 Mon Sep 17 00:00:00 2001 From: myuon Date: Mon, 18 Sep 2023 22:34:25 +0900 Subject: [PATCH 07/12] fix: address calculation --- memorydump/main.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/memorydump/main.go b/memorydump/main.go index 5530fb8e..95ae208a 100644 --- a/memorydump/main.go +++ b/memorydump/main.go @@ -32,7 +32,7 @@ func appendByteAsHex(bs []byte, b byte) []byte { return bs } -func hexdump(data []byte, offset int, writer io.Writer) { +func hexdump(data []byte, offset int64, writer io.Writer) { skip := false dataOffset := 0 @@ -41,8 +41,8 @@ func hexdump(data []byte, offset int, writer io.Writer) { unit := data[dataOffset:min(dataOffset+unitSize, len(data))] for i := 0; i < len(unit)/16; i += 1 { - addr := i * 16 - chunk := unit[addr:min(addr+16, len(unit))] + unitIndex := i * 16 + chunk := unit[unitIndex:min(unitIndex+16, len(unit))] hexVals := make([]byte, 0, 48) asciiVals := make([]byte, 0, 16) nonZeroFlag := false @@ -61,16 +61,18 @@ func hexdump(data []byte, offset int, writer io.Writer) { } } + address := int64(unitIndex) + int64(dataOffset) + offset + if nonZeroFlag { if skip { - fmt.Fprintf(writer, "%08x 00\n", addr+offset) + fmt.Fprintf(writer, "%08x 00\n", address) } - fmt.Fprintf(writer, "%08x %s |%s|\n", addr+offset, string(hexVals), string(asciiVals)) + fmt.Fprintf(writer, "%08x %s |%s|\n", address, string(hexVals), string(asciiVals)) skip = false } else { if !skip { - fmt.Fprintf(writer, "%08x 00\n", addr+offset) + fmt.Fprintf(writer, "%08x 00\n", address) fmt.Fprint(writer, "...\n") skip = true @@ -82,7 +84,7 @@ func hexdump(data []byte, offset int, writer io.Writer) { } if skip { - fmt.Fprintf(writer, "%08x 00\n", dataOffset+offset) + fmt.Fprintf(writer, "%08x 00\n", int64(dataOffset)+offset) } } @@ -104,7 +106,7 @@ func deletePreviousFiles(pattern string) { func main() { // ファイルパスとチャンクサイズを指定 filePath := "./build/memory/memory.bin" - chunkSize := 250 * 1024 * 1024 + chunkSize := int64(250 * 1024 * 1024) ext := filepath.Ext(filePath) baseName := strings.Replace(filepath.Base(filePath), ext, "", 1) dirPath := filepath.Dir(filePath) @@ -184,7 +186,7 @@ func main() { } writer := bufio.NewWriter(hexdumpFile) - hexdump(chunkData, index*chunkSize, writer) + hexdump(chunkData, int64(index)*chunkSize, writer) writer.Flush() hexdumpFile.Close() From 80d3fe78a979007e29bfaa434d1222f8b415db11 Mon Sep 17 00:00:00 2001 From: myuon Date: Mon, 18 Sep 2023 22:41:57 +0900 Subject: [PATCH 08/12] fix: data_section_offset must be a multiple of 8 --- quartz/compiler.qz | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quartz/compiler.qz b/quartz/compiler.qz index 3a09d5ed..f9bf5bc8 100644 --- a/quartz/compiler.qz +++ b/quartz/compiler.qz @@ -199,7 +199,8 @@ module Compiler { term = gen.fold_consts(term); - let code = gen.run(term, entrypoint, irgen.data_section_offset); + let offset = ((irgen.data_section_offset + 7) / 8) * 8; + let code = gen.run(term, entrypoint, offset); return code; } From 785d8c36e166f60943446175a530ee348e02c2cc Mon Sep 17 00:00:00 2001 From: myuon Date: Tue, 19 Sep 2023 01:18:12 +0900 Subject: [PATCH 09/12] feat: IrTermWriter --- .gitignore | 2 + quartz/compiler.qz | 2 + quartz/ir.qz | 244 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 247 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8c588812..c9674a8e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ quartz-debugger.json # already existing elements were commented out #/target + +*.ir diff --git a/quartz/compiler.qz b/quartz/compiler.qz index f9bf5bc8..bcc9f853 100644 --- a/quartz/compiler.qz +++ b/quartz/compiler.qz @@ -191,6 +191,8 @@ module Compiler { term = transform_let_call(term); + // file_write("quartz.ir", term.to_string()); + let gen = Generator::new(validate_address); gen.set_globals(typechecker.globals); gen.set_strings(irgen.strings.strings); diff --git a/quartz/ir.qz b/quartz/ir.qz index 497cb8fc..e7fc1c55 100644 --- a/quartz/ir.qz +++ b/quartz/ir.qz @@ -98,7 +98,10 @@ enum IrTerm { module IrTerm { fun to_string(self): string { - return derive::to_string_pretty(self); + let writer = IrTermWriter::new(); + writer.expression(self); + + return writer.to_string(); } fun ident(name: string): IrTerm { @@ -322,3 +325,242 @@ module IrType { } } +struct IrTermWriter { + writer: stringbuilder, + depth: i32, + index: i32, +} + +module IrTermWriter { + fun new(): IrTermWriter { + return IrTermWriter { + writer: stringbuilder::new(), + depth: 0, + index: 0, + }; + } + + fun to_string(self): string { + return self.writer.to_string(); + } + + fun start(self) { + self.new_statement(); + self.write("("); + self.depth = self.depth + 1; + self.index = 0; + } + + fun end(self) { + self.depth = self.depth - 1; + self.index = 0; + self.write(")"); + } + + fun write(self, str: string) { + if self.index == 0 { + self.writer.append(str); + } else { + self.writer.append(" "); + self.writer.append(str); + } + self.index = self.index + 1; + } + + fun new_statement(self) { + if self.index > 0 { + self.writer.append("\n"); + self.writer.append(" ".repeat(self.depth * 4)); + } + self.index = 0; + } + + fun expression(self, term: IrTerm) { + if term.t_nil != nil { + self.write("nil"); + } else if term.t_i32 != nil { + self.write("{}".format(term.t_i32!.to_string())); + } else if term.t_ident != nil { + self.write("${}".format(term.t_ident!)); + } else if term.t_func != nil { + self.start(); + self.write("func ${}".format(term.t_func!.name)); + for param in term.t_func!.params { + self.write("(params ${} {})".format(param.name, param.type_.to_string())); + } + self.write("(result {})".format(term.t_func!.result_type.to_string())); + + self.start(); + for t in term.t_func!.body { + self.expression(t); + } + self.end(); + + self.end(); + } else if term.t_call != nil { + self.start(); + self.write("call"); + self.expression(term.t_call!.callee); + for arg in term.t_call!.args { + self.expression(arg); + } + self.end(); + } else if term.t_let != nil { + self.start(); + self.write("let ${} {}".format(term.t_let!.name, term.t_let!.type_.to_string())); + self.expression(term.t_let!.value); + self.end(); + } else if term.t_return != nil { + self.start(); + self.write("return"); + self.expression(term.t_return!.value); + self.end(); + } else if term.t_global_let != nil { + self.start(); + self.write("global-let ${} {}".format(term.t_global_let!.name, term.t_global_let!.type_.to_string())); + self.expression(term.t_global_let!.value); + self.end(); + } else if term.t_module != nil { + self.start(); + self.write("module"); + for element in term.t_module!.elements { + self.expression(element); + } + self.end(); + } else if term.t_assign != nil { + self.start(); + self.write("assign"); + self.write("${}".format(term.t_assign!.lhs)); + self.expression(term.t_assign!.rhs); + self.end(); + } else if term.t_if != nil { + self.start(); + self.write("if"); + self.expression(term.t_if!.condition); + self.expression(term.t_if!.then_term); + self.expression(term.t_if!.else_term); + self.end(); + } else if term.t_while != nil { + self.start(); + self.write("while"); + self.expression(term.t_while!.condition); + self.expression(term.t_while!.block); + if term.t_while!.cleanup != nil { + self.expression(term.t_while!.cleanup!); + } + } else if term.t_seq != nil { + self.start(); + self.write("seq"); + for t in term.t_seq!.terms { + self.expression(t); + } + self.end(); + } else if term.t_store != nil { + self.start(); + self.write("store"); + self.write(term.t_store!.type_.to_string()); + self.expression(term.t_store!.address); + self.expression(term.t_store!.offset); + self.expression(term.t_store!.value); + self.write(term.t_store!.raw_offset.to_string()); + self.end(); + } else if term.t_load != nil { + self.start(); + self.write("load"); + self.write(term.t_load!.type_.to_string()); + self.expression(term.t_load!.address); + self.expression(term.t_load!.offset); + self.write(term.t_load!.raw_offset.to_string()); + self.end(); + } else if term.t_sizeof != nil { + self.start(); + self.write("sizeof"); + self.write(term.t_sizeof!.type_.to_string()); + self.end(); + } else if term.t_string != nil { + self.write("(string {})".format(term.t_string!.to_string())); + } else if term.t_data != nil { + self.start(); + self.write("data"); + self.write(term.t_data!.data); + self.write(term.t_data!.offset.to_string()); + self.end(); + } else if term.t_discard != nil { + self.start(); + self.write("discard"); + self.expression(term.t_discard!); + self.end(); + } else if term.t_inst != nil { + self.start(); + self.write("inst"); + self.write(term.t_inst!); + self.end(); + } else if term.t_bool != nil { + self.write(term.t_bool!.to_string()); + } else if term.t_and != nil { + self.start(); + self.write("and"); + self.expression(term.t_and!.lhs); + self.expression(term.t_and!.rhs); + self.end(); + } else if term.t_or != nil { + self.start(); + self.write("or"); + self.expression(term.t_or!.lhs); + self.expression(term.t_or!.rhs); + self.end(); + } else if term.t_continue != nil { + self.write("continue"); + } else if term.t_break != nil { + self.write("break"); + } else if term.t_size != nil { + self.start(); + self.write("size"); + self.write(term.t_size!.to_string()); + self.end(); + } else if term.t_u32 != nil { + self.write(term.t_u32!.to_string()); + } else if term.t_comment != nil { + self.start(); + self.write("comment"); + self.write(term.t_comment!); + self.end(); + } else if term.t_type_rep != nil { + self.start(); + self.write("type-rep"); + self.write(term.t_type_rep!.to_string()); + self.end(); + } else if term.t_import != nil { + self.start(); + self.write("import"); + self.write(term.t_import!.namespace); + self.write(term.t_import!.wrap_name); + self.write(term.t_import!.import_name); + + self.start(); + self.write("func-type"); + for param in term.t_import!.func_type.params { + self.write("(param {})".format(param)); + } + self.write("(result {})".format(term.t_import!.func_type.result_type)); + self.end(); + + self.end(); + } else if term.t_wasm_func != nil { + self.start(); + self.write("wasm-func"); + self.write(term.t_wasm_func!.name); + self.write(term.t_wasm_func!.wrapping_name); + + self.start(); + self.write("params"); + for param in term.t_wasm_func!.params { + self.write("(param ${} {} {})".format(param.name, param.quartz_type.to_string(), param.wasm_type)); + } + self.write("(result ${} {})".format(term.t_wasm_func!.result_type.quartz_type.to_string(), term.t_wasm_func!.result_type.wasm_type)); + self.end(); + + self.end(); + } + } +} From 03ad6b4d1dc61c1c8cf1e1a29167e25ec3c3f1c1 Mon Sep 17 00:00:00 2001 From: myuon Date: Tue, 19 Sep 2023 02:14:28 +0900 Subject: [PATCH 10/12] feat: register builtin, declared types in type_reps --- quartz/ir_code_gen.qz | 113 +++++++++++++++++++++++++++++++++++++--- quartz/std.qz | 1 + tests/cases/gc/test2.qz | 10 +++- 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/quartz/ir_code_gen.qz b/quartz/ir_code_gen.qz index 53946fff..26dad207 100644 --- a/quartz/ir_code_gen.qz +++ b/quartz/ir_code_gen.qz @@ -221,9 +221,112 @@ module IrCodeGenerator { } fun run(self, m: Module): IrTerm or error { + // Register builtin types + self.type_reps.get_or_insert( + InternalTypeRep::from_ir_type(IrType { + t_nil: true, + }), + ); + self.type_reps.get_or_insert( + InternalTypeRep::from_ir_type(IrType { + t_i32: true, + }), + ); + self.type_reps.get_or_insert( + InternalTypeRep::from_ir_type(IrType { + t_address: true, + }), + ); + self.type_reps.get_or_insert( + InternalTypeRep::from_ir_type(IrType { + t_byte: true, + }), + ); + self.type_reps.get_or_insert( + InternalTypeRep::from_ir_type(IrType { + t_any: true, + }), + ); + self.type_reps.get_or_insert( + InternalTypeRep::from_ir_type(IrType { + t_bool: true, + }), + ); + self.type_reps.get_or_insert( + InternalTypeRep::from_name( + "ptr", + make[vec[InternalTypeRep]](InternalTypeRep::from_ir_type(IrType { + t_byte: true, + })), + ), + ); + self.type_reps.get_or_insert( + InternalTypeRep::from_name( + "ptr", + make[vec[InternalTypeRep]](InternalTypeRep::from_ir_type(IrType { + t_byte: true, + })), + ), + ); + self.type_reps.get_or_insert( + InternalTypeRep::from_name( + "ptr", + make[vec[InternalTypeRep]](InternalTypeRep::from_ir_type(IrType { + t_i32: true, + })), + ), + ); + + // Register declared types + for k in self.globals.list_keys() { + let v = self.globals.at(k); + if v.data.t_struct != nil { + let fields = make[vec[struct { + name: string, + type_: InternalTypeRep, + }]](); + for field in v.data.t_struct!.fields { + fields.push(struct { + name: field.data.name, + type_: InternalTypeRep::from_ir_type(IrType::new(field.data.type_)), + }); + } + + let rep = InternalTypeRep { + kind: typeRepKindStruct, + name: k, + params: make[vec[InternalTypeRep]](), + fields: fields, + }; + + self.type_reps.get_or_insert(rep); + } else if v.data.t_enum != nil { + let fields = make[vec[struct { + name: string, + type_: InternalTypeRep, + }]](); + for field in v.data.t_enum!.fields { + fields.push(struct { + name: field.data.name, + type_: InternalTypeRep::from_ir_type(IrType::new(field.data.type_)), + }); + } + + let rep = InternalTypeRep { + kind: typeRepKindEnum, + name: k, + params: make[vec[InternalTypeRep]](), + fields: fields, + }; + + self.type_reps.get_or_insert(rep); + } + } + let decls = self.module_(m).try; decls.push(self.generate_prepare_type_reps().try); decls.extend(self.generate_prepare_strings().try); + decls.push(IrTerm { t_func: IrFunc { name: "reflection_get_type_rep_id", @@ -434,9 +537,9 @@ module IrCodeGenerator { wasm_type: wasm_type, }?; } else { - return _ or error::new("invalid result_type, got {}".format( - d.declare_wrap!.result_type.to_string(), - )); + return _ or error::new( + "invalid result_type, got {}".format(d.declare_wrap!.result_type.to_string()), + ); } return IrTerm { @@ -484,9 +587,7 @@ module IrCodeGenerator { } else if d.declare_wrap!.result_type.t_ident != nil && d.declare_wrap!.result_type.t_ident! == "wasm_externref" { result_type = "externref"; } else { - return _ or error::new("invalid result_type, {}".format( - d.declare_wrap!.result_type.to_string(), - )); + return _ or error::new("invalid result_type, {}".format(d.declare_wrap!.result_type.to_string())); } return IrTerm { diff --git a/quartz/std.qz b/quartz/std.qz index c9b416c9..69917542 100644 --- a/quartz/std.qz +++ b/quartz/std.qz @@ -1738,6 +1738,7 @@ fun mark(value: any) { header.set_is_marked(true); debug(header); + debug(header.get_type_rep()); let rep = (header.get_type_rep() as ptr[TypeRep]).at(0); // FIXME: Why is this necessary? diff --git a/tests/cases/gc/test2.qz b/tests/cases/gc/test2.qz index aea63c0b..0b63947a 100644 --- a/tests/cases/gc/test2.qz +++ b/tests/cases/gc/test2.qz @@ -13,9 +13,17 @@ fun f(arg1: P): P { } fun main(): bool { - let p = P { x: 42 }; + debug(type_reps_ptr); + for i in 0..type_reps_count { + debug(type_reps_ptr.at(i)); + } + abort(); + let p = P { + x: 42, + }; let q = f(p); let object = Header::from_data_ptr(q as ptr[byte]); return object.get_is_free(); } + From 78ca8c1faedddc00bef5dbfe0a9d887c6c309775 Mon Sep 17 00:00:00 2001 From: myuon Date: Tue, 19 Sep 2023 02:24:15 +0900 Subject: [PATCH 11/12] fix: TypeReps for slice[TypeRep], slice[string] --- quartz/ir_code_gen.qz | 69 +++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/quartz/ir_code_gen.qz b/quartz/ir_code_gen.qz index 26dad207..96eb9743 100644 --- a/quartz/ir_code_gen.qz +++ b/quartz/ir_code_gen.qz @@ -252,30 +252,30 @@ module IrCodeGenerator { t_bool: true, }), ); - self.type_reps.get_or_insert( - InternalTypeRep::from_name( - "ptr", - make[vec[InternalTypeRep]](InternalTypeRep::from_ir_type(IrType { + self.type_reps.get_or_insert(InternalTypeRep::from_name( + "ptr", + make[vec[InternalTypeRep]]( + InternalTypeRep::from_ir_type(IrType { t_byte: true, - })), + }), ), - ); - self.type_reps.get_or_insert( - InternalTypeRep::from_name( - "ptr", - make[vec[InternalTypeRep]](InternalTypeRep::from_ir_type(IrType { + )); + self.type_reps.get_or_insert(InternalTypeRep::from_name( + "ptr", + make[vec[InternalTypeRep]]( + InternalTypeRep::from_ir_type(IrType { t_byte: true, - })), + }), ), - ); - self.type_reps.get_or_insert( - InternalTypeRep::from_name( - "ptr", - make[vec[InternalTypeRep]](InternalTypeRep::from_ir_type(IrType { + )); + self.type_reps.get_or_insert(InternalTypeRep::from_name( + "ptr", + make[vec[InternalTypeRep]]( + InternalTypeRep::from_ir_type(IrType { t_i32: true, - })), + }), ), - ); + )); // Register declared types for k in self.globals.list_keys() { @@ -2715,42 +2715,53 @@ module IrCodeGenerator { elements.push(IrTerm::i32(rep.params.length)); let type_rep_terms = make[vec[struct { - label: string?, type_: IrType, term: IrTerm, }]](); for p in rep.params { type_rep_terms.push(struct { - label: nil, type_: IrType { t_address: true, }, term: self.write_type_rep(p).try, }); } - elements.push( - self.generate_array("slice", make[vec[IrType]](), type_rep_terms).context(struct { - context: "write_type_rep", - rep: rep, - }).try, - ); + elements.push(self.generate_array_with_rep( + InternalTypeRep::from_name( + "slice", + make[vec[InternalTypeRep]](InternalTypeRep::from_name( + "TypeRep", + make[vec[InternalTypeRep]](), + )), + ), + type_rep_terms, + ).context(struct { + context: "write_type_rep", + rep: rep, + }).try); elements.push(IrTerm::i32(rep.fields.length)); let field_terms = make[vec[struct { - label: string?, type_: IrType, term: IrTerm, }]](); for t in rep.fields { field_terms.push(struct { - label: t.name?, type_: IrType { t_address: true, }, term: self.register_string(t.name), }); } - elements.push(self.generate_array("slice", make[vec[IrType]](), field_terms).try); + elements.push(self.generate_array_with_rep( + InternalTypeRep::from_name( + "slice", + make[vec[InternalTypeRep]]( + InternalTypeRep::from_name("string", make[vec[InternalTypeRep]]()), + ), + ), + field_terms, + ).try); let var_name = format("type_rep_{}", self.counter.to_string()); self.counter = self.counter + 1; From 62302a1e0ec3dbc33741a2babf133428e5ff9c3e Mon Sep 17 00:00:00 2001 From: myuon Date: Tue, 19 Sep 2023 02:32:02 +0900 Subject: [PATCH 12/12] feat: register types for write_type_rep --- quartz/ir_code_gen.qz | 14 ++++++++++++++ quartz/std.qz | 7 ------- tests/cases/gc/test2.qz | 5 ----- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/quartz/ir_code_gen.qz b/quartz/ir_code_gen.qz index 96eb9743..0c41c27f 100644 --- a/quartz/ir_code_gen.qz +++ b/quartz/ir_code_gen.qz @@ -276,6 +276,16 @@ module IrCodeGenerator { }), ), )); + self.type_reps.get_or_insert(InternalTypeRep::from_name("string", make[vec[InternalTypeRep]]())); + self.type_reps.get_or_insert(InternalTypeRep::from_name( + "slice", + make[vec[InternalTypeRep]](InternalTypeRep::from_name("string", make[vec[InternalTypeRep]]())), + )); + self.type_reps.get_or_insert(InternalTypeRep::from_name("TypeRep", make[vec[InternalTypeRep]]())); + self.type_reps.get_or_insert(InternalTypeRep::from_name( + "slice", + make[vec[InternalTypeRep]](InternalTypeRep::from_name("TypeRep", make[vec[InternalTypeRep]]())), + )); // Register declared types for k in self.globals.list_keys() { @@ -324,6 +334,8 @@ module IrCodeGenerator { } let decls = self.module_(m).try; + + let type_rep_count = self.type_reps.type_reps.length; decls.push(self.generate_prepare_type_reps().try); decls.extend(self.generate_prepare_strings().try); @@ -411,6 +423,8 @@ module IrCodeGenerator { }); } + assert_eq(self.type_reps.type_reps.length, type_rep_count); + return IrTerm { t_module: struct { elements: result, diff --git a/quartz/std.qz b/quartz/std.qz index 69917542..3c81f96e 100644 --- a/quartz/std.qz +++ b/quartz/std.qz @@ -1737,14 +1737,7 @@ fun mark(value: any) { header.set_is_marked(true); - debug(header); - debug(header.get_type_rep()); - let rep = (header.get_type_rep() as ptr[TypeRep]).at(0); - // FIXME: Why is this necessary? - // if !types::is_pointer(rep) { - // return; - // } if rep.nilptr != nil { debug(error_invalid_type); debug(rep.nilptr); diff --git a/tests/cases/gc/test2.qz b/tests/cases/gc/test2.qz index 0b63947a..f0f0c119 100644 --- a/tests/cases/gc/test2.qz +++ b/tests/cases/gc/test2.qz @@ -13,11 +13,6 @@ fun f(arg1: P): P { } fun main(): bool { - debug(type_reps_ptr); - for i in 0..type_reps_count { - debug(type_reps_ptr.at(i)); - } - abort(); let p = P { x: 42, };