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/justfile b/justfile index a32d58b9..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}} - 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..95ae208a --- /dev/null +++ b/memorydump/main.go @@ -0,0 +1,198 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" +) + +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 int64, 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 { + unitIndex := i * 16 + chunk := unit[unitIndex:min(unitIndex+16, len(unit))] + hexVals := make([]byte, 0, 48) + asciiVals := make([]byte, 0, 16) + nonZeroFlag := false + for _, b := range chunk { + 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 + } + } + + address := int64(unitIndex) + int64(dataOffset) + offset + + if nonZeroFlag { + if skip { + fmt.Fprintf(writer, "%08x 00\n", address) + } + + fmt.Fprintf(writer, "%08x %s |%s|\n", address, string(hexVals), string(asciiVals)) + skip = false + } else { + if !skip { + fmt.Fprintf(writer, "%08x 00\n", address) + fmt.Fprint(writer, "...\n") + + skip = true + } + } + } + + dataOffset += unitSize + } + + if skip { + fmt.Fprintf(writer, "%08x 00\n", int64(dataOffset)+offset) + } +} + +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 := int64(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) + } + + wg := sync.WaitGroup{} + for index, chunkFilePath := range chunkFilePaths { + wg.Add(1) + go func(index int, chunkFilePath string) { + defer wg.Done() + + 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, int64(index)*chunkSize, writer) + writer.Flush() + hexdumpFile.Close() + + fmt.Printf("Created hexdump file: %s\n", hexdumpFilePath) + }(index, chunkFilePath) + } + + wg.Wait() +} 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/quartz/compiler.qz b/quartz/compiler.qz index 3a09d5ed..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); @@ -199,7 +201,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; } 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/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(); + } + } +} diff --git a/quartz/ir_code_gen.qz b/quartz/ir_code_gen.qz index 53946fff..0c41c27f 100644 --- a/quartz/ir_code_gen.qz +++ b/quartz/ir_code_gen.qz @@ -221,9 +221,124 @@ 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, + }), + ), + )); + 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() { + 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; + + 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); + decls.push(IrTerm { t_func: IrFunc { name: "reflection_get_type_rep_id", @@ -308,6 +423,8 @@ module IrCodeGenerator { }); } + assert_eq(self.type_reps.type_reps.length, type_rep_count); + return IrTerm { t_module: struct { elements: result, @@ -434,9 +551,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 +601,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 { @@ -2614,42 +2729,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; diff --git a/quartz/std.qz b/quartz/std.qz index e32c2d38..3c81f96e 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(); @@ -500,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, @@ -828,10 +839,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 +1270,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 +1465,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 +1516,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 +1582,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, + }; } } } @@ -1719,10 +1738,6 @@ fun mark(value: any) { header.set_is_marked(true); 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); @@ -1780,7 +1795,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 +1803,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 +1811,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 +1860,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 +1902,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 +1932,4 @@ fun assert(a: bool) { panic("assert failed"); } } + 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); diff --git a/src/runtime.rs b/src/runtime.rs index 739d973c..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,17 +63,24 @@ 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| { 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 { - 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(); @@ -64,9 +104,16 @@ impl Runtime { wasi_func_env.initialize(&mut store, &instance)?; let main = instance.exports.get_function("main")?; - let result = main.call(&mut store, &[])?; + let result = main + .call(&mut store, &[]) + .map_err(|err| anyhow!("calling main: {:?}", err)); + + let memory = instance.exports.get_memory("memory")?; + let view = memory.view(&mut store); + + Runtime::dump_memory(view)?; - Ok(result) + result } pub fn run(&mut self, input: &str) -> Result> { diff --git a/tests/cases/gc/test2.qz b/tests/cases/gc/test2.qz index aea63c0b..f0f0c119 100644 --- a/tests/cases/gc/test2.qz +++ b/tests/cases/gc/test2.qz @@ -13,9 +13,12 @@ fun f(arg1: P): P { } fun main(): bool { - let p = P { x: 42 }; + let p = P { + x: 42, + }; let q = f(p); let object = Header::from_data_ptr(q as ptr[byte]); return object.get_is_free(); } +