floats
This commit is contained in:
@@ -137,6 +137,17 @@ _builtin_set64:
|
|||||||
mov [rdi], rsi
|
mov [rdi], rsi
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
section .text._builtin_cvtsi2sd
|
||||||
|
_builtin_cvtsi2sd:
|
||||||
|
cvtsi2sd xmm0, rdi
|
||||||
|
movq rax, xmm0
|
||||||
|
ret
|
||||||
|
|
||||||
|
section .text._builtin_cvttsd2si
|
||||||
|
_builtin_cvttsd2si:
|
||||||
|
cvttsd2si rax, xmm0
|
||||||
|
ret
|
||||||
|
|
||||||
section .text._builtin_syscall
|
section .text._builtin_syscall
|
||||||
_builtin_syscall:
|
_builtin_syscall:
|
||||||
mov rax, rdi
|
mov rax, rdi
|
||||||
@@ -682,8 +693,11 @@ _builtin_environ:
|
|||||||
emit!(&mut self.output, " push rax");
|
emit!(&mut self.output, " push rax");
|
||||||
}
|
}
|
||||||
|
|
||||||
let arg_count = args.len();
|
let arg_types: Vec<String> = args
|
||||||
self.emit_call_setup(arg_count);
|
.iter()
|
||||||
|
.map(|a| self.expr_types[&a.id].clone())
|
||||||
|
.collect();
|
||||||
|
self.emit_call_setup(&arg_types);
|
||||||
|
|
||||||
if let ExprKind::Variable(callee_name) = &callee.kind {
|
if let ExprKind::Variable(callee_name) = &callee.kind {
|
||||||
if self
|
if self
|
||||||
@@ -704,7 +718,7 @@ _builtin_environ:
|
|||||||
emit!(&mut self.output, " call rax");
|
emit!(&mut self.output, " call rax");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emit_call_cleanup(arg_count);
|
self.emit_call_cleanup(args.len());
|
||||||
}
|
}
|
||||||
ExprKind::ArrayLiteral(exprs) => {
|
ExprKind::ArrayLiteral(exprs) => {
|
||||||
emit!(&mut self.output, " mov rdi, 24");
|
emit!(&mut self.output, " mov rdi, 24");
|
||||||
@@ -805,33 +819,39 @@ _builtin_environ:
|
|||||||
emit!(&mut self.output, " push rax");
|
emit!(&mut self.output, " push rax");
|
||||||
}
|
}
|
||||||
|
|
||||||
let arg_count = 1 + args.len();
|
let mut arg_types = vec![];
|
||||||
self.emit_call_setup(arg_count);
|
arg_types.push(receiver_type.clone());
|
||||||
|
arg_types.extend(args.iter().map(|a| self.expr_types[&a.id].clone()));
|
||||||
|
|
||||||
|
self.emit_call_setup(&arg_types);
|
||||||
emit!(&mut self.output, " call {}", func_name);
|
emit!(&mut self.output, " call {}", func_name);
|
||||||
self.emit_call_cleanup(arg_count);
|
self.emit_call_cleanup(1 + args.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_call_setup(&mut self, arg_count: usize) {
|
fn emit_call_setup(&mut self, arg_types: &[String]) {
|
||||||
if arg_count <= 6 {
|
let arg_count = arg_types.len();
|
||||||
for i in (0..arg_count).rev() {
|
|
||||||
emit!(&mut self.output, " pop {}", REGISTERS[i]);
|
let to_register = arg_count.min(6);
|
||||||
}
|
let mut fp_idx = 0;
|
||||||
} else {
|
let mut int_idx = 0;
|
||||||
for (i, reg) in REGISTERS.iter().enumerate() {
|
for (i, arg_type) in arg_types.iter().enumerate().take(to_register) {
|
||||||
let offset = 8 * (arg_count - 1 - i);
|
let offset = 8 * (arg_count - 1 - i);
|
||||||
emit!(
|
emit!(&mut self.output, " mov rax, QWORD [rsp + {}]", offset);
|
||||||
&mut self.output,
|
if arg_type == "f64" {
|
||||||
" mov {}, QWORD [rsp + {}]",
|
emit!(&mut self.output, " movq xmm{}, rax", fp_idx);
|
||||||
reg,
|
fp_idx += 1;
|
||||||
offset
|
} else {
|
||||||
);
|
emit!(&mut self.output, " mov {}, rax", REGISTERS[int_idx]);
|
||||||
|
int_idx += 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: since all zern values are 64bit large we currently cannot call
|
// TODO: since all zern values are 64bit large we currently cannot call
|
||||||
// external functions that expect a non-64bit value past the 6th argument
|
// external functions that expect a non-64bit value past the 6th argument
|
||||||
let num_stack = arg_count - 6;
|
let num_stack = arg_count.saturating_sub(6);
|
||||||
for i in 0..num_stack {
|
for i in 0..num_stack {
|
||||||
let arg_idx = arg_count - 1 - i;
|
let arg_idx = arg_count - 1 - i;
|
||||||
let offset = 8 * (arg_count - 1 - arg_idx);
|
let offset = 8 * (arg_count - 1 - arg_idx);
|
||||||
@@ -842,13 +862,22 @@ _builtin_environ:
|
|||||||
);
|
);
|
||||||
emit!(&mut self.output, " push rax");
|
emit!(&mut self.output, " push rax");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit!(&mut self.output, " mov al, {}", fp_idx);
|
||||||
|
|
||||||
|
if num_stack == 0 {
|
||||||
|
emit!(&mut self.output, " add rsp, {}", 8 * to_register);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_call_cleanup(&mut self, arg_count: usize) {
|
fn emit_call_cleanup(&mut self, arg_count: usize) {
|
||||||
if arg_count > 6 {
|
let num_stack = arg_count.saturating_sub(6);
|
||||||
emit!(&mut self.output, " add rsp, {}", 8 * (arg_count - 6));
|
if num_stack > 0 {
|
||||||
emit!(&mut self.output, " add rsp, {}", 8 * arg_count);
|
emit!(
|
||||||
|
&mut self.output,
|
||||||
|
" add rsp, {}",
|
||||||
|
8 * (arg_count + num_stack)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -309,18 +309,6 @@ func io.read_line[] : str
|
|||||||
b->destroy()
|
b->destroy()
|
||||||
return s
|
return s
|
||||||
|
|
||||||
func io.file_exists[path: str] : bool
|
|
||||||
return _builtin_syscall(SYS_faccessat, AT_FDCWD, path, F_OK, 0) == 0
|
|
||||||
|
|
||||||
func io.is_a_directory[path: str] : bool
|
|
||||||
st := _stackalloc(256) // it has 21 mixed-size fields so ptr for now
|
|
||||||
|
|
||||||
rc := _builtin_syscall(SYS_newfstatat, AT_FDCWD, path, st, 0)
|
|
||||||
if rc != 0
|
|
||||||
return false
|
|
||||||
|
|
||||||
return (mem.read32(st + 24) & S_IFMT) == S_IFDIR
|
|
||||||
|
|
||||||
func io.read_text_file[path: str] : str, bool
|
func io.read_text_file[path: str] : str, bool
|
||||||
fd := _builtin_syscall(SYS_openat, AT_FDCWD, path, O_RDONLY, 0)
|
fd := _builtin_syscall(SYS_openat, AT_FDCWD, path, O_RDONLY, 0)
|
||||||
if fd < 0
|
if fd < 0
|
||||||
@@ -929,6 +917,12 @@ func Array._partition[arr: Array, low: i64, high: i64] : i64
|
|||||||
arr->set(high, temp)
|
arr->set(high, temp)
|
||||||
return i + 1
|
return i + 1
|
||||||
|
|
||||||
|
func Array.contains_str[arr: Array, s: str] : bool
|
||||||
|
for i in 0..arr->size
|
||||||
|
if (arr->nth(i) as str)->equal(s)
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
func Array.count[arr: Array, item: any] : i64
|
func Array.count[arr: Array, item: any] : i64
|
||||||
count := 0
|
count := 0
|
||||||
for i in 0..arr->size
|
for i in 0..arr->size
|
||||||
@@ -1108,6 +1102,18 @@ func os.run_shell_command[command: str] : i64, bool
|
|||||||
else
|
else
|
||||||
return -(st & 0x7f), true
|
return -(st & 0x7f), true
|
||||||
|
|
||||||
|
func os.file_exists[path: str] : bool
|
||||||
|
return _builtin_syscall(SYS_faccessat, AT_FDCWD, path, F_OK, 0) == 0
|
||||||
|
|
||||||
|
func os.is_a_directory[path: str] : bool
|
||||||
|
st := _stackalloc(256) // it has 21 mixed-size fields so ptr for now
|
||||||
|
|
||||||
|
rc := _builtin_syscall(SYS_newfstatat, AT_FDCWD, path, st, 0)
|
||||||
|
if rc != 0
|
||||||
|
return false
|
||||||
|
|
||||||
|
return (mem.read32(st + 24) & S_IFMT) == S_IFDIR
|
||||||
|
|
||||||
func os.list_directory[path: str] : Array, bool
|
func os.list_directory[path: str] : Array, bool
|
||||||
fd := _builtin_syscall(SYS_openat, AT_FDCWD, path, O_RDONLY, 0)
|
fd := _builtin_syscall(SYS_openat, AT_FDCWD, path, O_RDONLY, 0)
|
||||||
if fd < 0
|
if fd < 0
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ impl SymbolTable {
|
|||||||
"_builtin_set64".into(),
|
"_builtin_set64".into(),
|
||||||
FnType::new("void", vec!["ptr", "i64"]),
|
FnType::new("void", vec!["ptr", "i64"]),
|
||||||
),
|
),
|
||||||
|
("_builtin_cvtsi2sd".into(), FnType::new("f64", vec!["i64"])),
|
||||||
|
("_builtin_cvttsd2si".into(), FnType::new("i64", vec!["f64"])),
|
||||||
("_builtin_syscall".into(), FnType::new_variadic("i64")),
|
("_builtin_syscall".into(), FnType::new_variadic("i64")),
|
||||||
("_builtin_environ".into(), FnType::new("ptr", vec![])),
|
("_builtin_environ".into(), FnType::new("ptr", vec![])),
|
||||||
("_var_arg".into(), FnType::new("any", vec!["i64"])),
|
("_var_arg".into(), FnType::new("any", vec!["i64"])),
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ macro_rules! expect_types {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static BUILTIN_TYPES: [&str; 7] = ["void", "u8", "i64", "str", "bool", "ptr", "any"];
|
static BUILTIN_TYPES: [&str; 8] = ["void", "u8", "i64", "f64", "str", "bool", "ptr", "any"];
|
||||||
|
|
||||||
pub struct Env {
|
pub struct Env {
|
||||||
scopes: Vec<HashMap<String, Type>>,
|
scopes: Vec<HashMap<String, Type>>,
|
||||||
@@ -300,6 +300,9 @@ impl<'a> TypeChecker<'a> {
|
|||||||
"unrecognized type: ".to_owned() + &return_type
|
"unrecognized type: ".to_owned() + &return_type
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if return_type == "f64" {
|
||||||
|
return error!(&return_types[0].loc, "returning f64 not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
self.current_function_return_type = return_type.clone();
|
self.current_function_return_type = return_type.clone();
|
||||||
|
|
||||||
@@ -314,6 +317,12 @@ impl<'a> TypeChecker<'a> {
|
|||||||
"unrecognized type: ".to_owned() + ¶m.var_type.lexeme
|
"unrecognized type: ".to_owned() + ¶m.var_type.lexeme
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if param.var_type.lexeme == "f64" {
|
||||||
|
return error!(
|
||||||
|
¶m.var_name.loc,
|
||||||
|
"f64 params not implemented yet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
env.define_var(
|
env.define_var(
|
||||||
param.var_name.lexeme.clone(),
|
param.var_name.lexeme.clone(),
|
||||||
@@ -549,6 +558,12 @@ impl<'a> TypeChecker<'a> {
|
|||||||
Ok(field.field_type.clone())
|
Ok(field.field_type.clone())
|
||||||
}
|
}
|
||||||
ExprKind::Cast { expr, type_name } => {
|
ExprKind::Cast { expr, type_name } => {
|
||||||
|
if type_name.lexeme == "f64" {
|
||||||
|
return error!(
|
||||||
|
&type_name.loc,
|
||||||
|
"use _builtin_cvtsi2sd and _builtin_cvttsd2si to cast to f64"
|
||||||
|
);
|
||||||
|
}
|
||||||
self.typecheck_expr(env, expr)?;
|
self.typecheck_expr(env, expr)?;
|
||||||
if !self.is_valid_type_name(&type_name.lexeme) {
|
if !self.is_valid_type_name(&type_name.lexeme) {
|
||||||
return error!(
|
return error!(
|
||||||
|
|||||||
60
test.zr
60
test.zr
@@ -1,32 +1,52 @@
|
|||||||
func run_test[x: str] : void
|
struct TestRunner
|
||||||
build_blacklist := ["examples/euler", "examples/raylib.zr", "examples/chip8.zr", "examples/sqlite_todo.zr"]
|
build_blacklist: Array
|
||||||
run_blacklist := ["guess_number.zr", "tcp_server.zr", "udp_server.zr"]
|
run_blacklist: Array
|
||||||
|
custom_build_flags: HashMap
|
||||||
|
custom_run_args: HashMap
|
||||||
|
|
||||||
for i in 0..build_blacklist->size
|
func TestRunner.run_directory[tr: TestRunner, dir: str] : void
|
||||||
if x->equal(build_blacklist->nth(i))
|
~files, ok := os.list_directory(dir)
|
||||||
|
if !ok
|
||||||
|
panic("failed to open test directory")
|
||||||
|
for i in 0..files->size
|
||||||
|
joined := dir->concat("/")->concat(files->nth(i))
|
||||||
|
if os.is_a_directory(joined)
|
||||||
|
tr->run_directory(joined)
|
||||||
|
else
|
||||||
|
tr->run_test(joined)
|
||||||
|
|
||||||
|
func TestRunner.run_test[tr: TestRunner, x: str] : void
|
||||||
|
name := os.basename(x)
|
||||||
|
|
||||||
|
if tr->build_blacklist->contains_str(name)
|
||||||
io.printf("Skipping %s...\n", x)
|
io.printf("Skipping %s...\n", x)
|
||||||
return
|
return
|
||||||
|
|
||||||
io.printf("Building %s...\n", x)
|
io.printf("Building %s...\n", x)
|
||||||
|
|
||||||
|
build_cmd := "./target/release/zern "->concat(x)
|
||||||
|
~custom_build_flags, ok := tr->custom_build_flags->get(name)
|
||||||
|
if ok
|
||||||
|
build_cmd = build_cmd->concat(" ")->concat(custom_build_flags)
|
||||||
|
|
||||||
build_start_time := os.time()
|
build_start_time := os.time()
|
||||||
~status, ok := os.run_shell_command("./target/release/zern "->concat(x))
|
~status, ok := os.run_shell_command(build_cmd)
|
||||||
if !ok || status != 0
|
if !ok || status != 0
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
build_end_time := os.time()
|
build_end_time := os.time()
|
||||||
|
|
||||||
io.printf("%dms\n", build_end_time - build_start_time)
|
io.printf("%dms\n", build_end_time - build_start_time)
|
||||||
|
|
||||||
for i in 0..run_blacklist->size
|
if tr->run_blacklist->contains_str(name)
|
||||||
if x->contains(run_blacklist->nth(i))
|
|
||||||
io.printf("Skipping %s...\n", x)
|
io.printf("Skipping %s...\n", x)
|
||||||
return
|
return
|
||||||
|
|
||||||
io.printf("Running %s...\n", x)
|
io.printf("Running %s...\n", x)
|
||||||
|
|
||||||
run_cmd := "./out"
|
run_cmd := "./out"
|
||||||
if x->equal("examples/curl.zr")
|
~custom_run_args, ok := tr->custom_run_args->get(name)
|
||||||
run_cmd = run_cmd->concat(" http://example.com")
|
if ok
|
||||||
|
run_cmd = run_cmd->concat(" ")->concat(custom_run_args)
|
||||||
|
|
||||||
run_start_time := os.time()
|
run_start_time := os.time()
|
||||||
~status, ok := os.run_shell_command(run_cmd)
|
~status, ok := os.run_shell_command(run_cmd)
|
||||||
@@ -37,18 +57,20 @@ func run_test[x: str] : void
|
|||||||
|
|
||||||
io.printf("Running %s took %dms\n", x, run_end_time - run_start_time)
|
io.printf("Running %s took %dms\n", x, run_end_time - run_start_time)
|
||||||
|
|
||||||
func run_directory[dir: str] : void
|
|
||||||
~files, ok := os.list_directory(dir)
|
|
||||||
if !ok
|
|
||||||
panic("failed to open test directory")
|
|
||||||
for i in 0..files->size
|
|
||||||
run_test(dir->concat(files->nth(i)))
|
|
||||||
|
|
||||||
func main[] : i64
|
func main[] : i64
|
||||||
~status, ok := os.run_shell_command("cargo build --release")
|
~status, ok := os.run_shell_command("cargo build --release")
|
||||||
if !ok || status != 0
|
if !ok || status != 0
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
|
|
||||||
run_directory("examples/")
|
tr := new TestRunner
|
||||||
run_directory("examples/euler/")
|
tr->build_blacklist = []
|
||||||
|
tr->run_blacklist = ["raylib.zr", "sqlite_todo.zr", "guess_number.zr", "udp_server.zr", "chip8.zr", "tcp_server.zr"]
|
||||||
|
tr->custom_build_flags = HashMap.new()
|
||||||
|
tr->custom_build_flags->insert("raylib.zr", "-m -C \"-lraylib\"")
|
||||||
|
tr->custom_build_flags->insert("chip8.zr", "-m -C \"-lraylib\"")
|
||||||
|
tr->custom_build_flags->insert("sqlite_todo.zr", "-m -C \"-lsqlite3\"")
|
||||||
|
tr->custom_run_args = HashMap.new()
|
||||||
|
tr->custom_run_args->insert("curl.zr", "http://example.com")
|
||||||
|
|
||||||
|
tr->run_directory("examples")
|
||||||
io.println("DONE")
|
io.println("DONE")
|
||||||
|
|||||||
Reference in New Issue
Block a user