all: reimplement inline assembly (#8645)
parent
dd9f9c2718
commit
fafb035fb5
|
@ -196,11 +196,6 @@ pub fn (mut ts TestSession) test() {
|
|||
continue
|
||||
}
|
||||
}
|
||||
$if tinyc {
|
||||
if file.contains('asm') {
|
||||
continue
|
||||
}
|
||||
}
|
||||
remaining_files << dot_relative_file
|
||||
}
|
||||
remaining_files = vtest.filter_vtest_only(remaining_files, fix_slashes: false)
|
||||
|
|
|
@ -3,6 +3,7 @@ module main
|
|||
import os
|
||||
import os.cmdline
|
||||
import testing
|
||||
import v.pref
|
||||
|
||||
fn main() {
|
||||
args := os.args.clone()
|
||||
|
@ -18,20 +19,35 @@ fn main() {
|
|||
eprintln('Use `v test-all` instead.')
|
||||
exit(1)
|
||||
}
|
||||
backend_pos := args_before.index('-b')
|
||||
backend := if backend_pos == -1 { '.c' } else { args_before[backend_pos + 1] } // this giant mess because closures are not implemented
|
||||
|
||||
mut ts := testing.new_test_session(args_before.join(' '))
|
||||
for targ in args_after {
|
||||
if os.exists(targ) && targ.ends_with('_test.v') {
|
||||
ts.files << targ
|
||||
continue
|
||||
}
|
||||
if os.is_dir(targ) {
|
||||
// Fetch all tests from the directory
|
||||
ts.files << os.walk_ext(targ.trim_right(os.path_separator), '_test.v')
|
||||
files, skip_files := should_test_dir(targ.trim_right(os.path_separator), backend)
|
||||
ts.files << files
|
||||
ts.skip_files << skip_files
|
||||
continue
|
||||
} else if os.exists(targ) {
|
||||
match should_test(targ, backend) {
|
||||
.test {
|
||||
ts.files << targ
|
||||
continue
|
||||
}
|
||||
.skip {
|
||||
ts.files << targ
|
||||
ts.skip_files << targ
|
||||
continue
|
||||
}
|
||||
.ignore {}
|
||||
}
|
||||
} else {
|
||||
eprintln('\nUnrecognized test file `$targ`.\n `v test` can only be used with folders and/or _test.v files.\n')
|
||||
show_usage()
|
||||
exit(1)
|
||||
}
|
||||
eprintln('\nUnrecognized test file `$targ` .\n `v test` can only be used with folders and/or _test.v files.\n')
|
||||
show_usage()
|
||||
exit(1)
|
||||
}
|
||||
testing.header('Testing...')
|
||||
ts.test()
|
||||
|
@ -52,3 +68,65 @@ fn show_usage() {
|
|||
println(' NB: you can also give many and mixed folder/ file_test.v arguments after `v test` .')
|
||||
println('')
|
||||
}
|
||||
|
||||
pub fn should_test_dir(path string, backend string) ([]string, []string) { // return is (files, skip_files)
|
||||
mut files := os.ls(path) or { return []string{}, []string{} }
|
||||
mut local_path_separator := os.path_separator
|
||||
if path.ends_with(os.path_separator) {
|
||||
local_path_separator = ''
|
||||
}
|
||||
mut res_files := []string{}
|
||||
mut skip_files := []string{}
|
||||
for file in files {
|
||||
p := path + local_path_separator + file
|
||||
if os.is_dir(p) && !os.is_link(p) {
|
||||
ret_files, ret_skip_files := should_test_dir(p, backend)
|
||||
res_files << ret_files
|
||||
skip_files << ret_skip_files
|
||||
} else if os.exists(p) {
|
||||
match should_test(p, backend) {
|
||||
.test {
|
||||
res_files << p
|
||||
}
|
||||
.skip {
|
||||
res_files << p
|
||||
skip_files << p
|
||||
}
|
||||
.ignore {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res_files, skip_files
|
||||
}
|
||||
|
||||
enum ShouldTestStatus {
|
||||
test // do test
|
||||
skip
|
||||
ignore
|
||||
}
|
||||
|
||||
fn should_test(path string, backend string) ShouldTestStatus {
|
||||
if path.ends_with('_test.v') {
|
||||
return .test
|
||||
}
|
||||
if path.ends_with('.v') && path.count('.') == 2 {
|
||||
if !path.all_before_last('.v').all_before_last('.').ends_with('_test') {
|
||||
return .ignore
|
||||
}
|
||||
backend_arg := path.all_before_last('.v').all_after_last('.')
|
||||
arch := pref.arch_from_string(backend_arg) or { pref.Arch._auto }
|
||||
if arch == pref.get_host_arch() {
|
||||
return .test
|
||||
} else if arch == ._auto {
|
||||
if backend_arg == 'c' { // .c.v
|
||||
return if backend == 'c' { ShouldTestStatus.test } else { ShouldTestStatus.skip }
|
||||
}
|
||||
if backend_arg == 'js' {
|
||||
return if backend == 'js' { ShouldTestStatus.test } else { ShouldTestStatus.skip }
|
||||
}
|
||||
} else {
|
||||
return .skip
|
||||
}
|
||||
}
|
||||
return .ignore
|
||||
}
|
||||
|
|
28
doc/docs.md
28
doc/docs.md
|
@ -3848,20 +3848,26 @@ To improve safety and maintainability, operator overloading is limited:
|
|||
are auto generated when the operators are defined though they must return the same type.
|
||||
|
||||
## Inline assembly
|
||||
|
||||
TODO: not implemented yet
|
||||
|
||||
```v failcompile
|
||||
fn main() {
|
||||
a := 10
|
||||
asm x64 {
|
||||
mov eax, [a]
|
||||
add eax, 10
|
||||
mov [a], eax
|
||||
}
|
||||
<!-- ignore because it doesn't pass fmt test (why?) -->
|
||||
```v ignore
|
||||
a := 100
|
||||
b := 20
|
||||
mut c := 0
|
||||
asm amd64 {
|
||||
mov eax, a
|
||||
add eax, b
|
||||
mov c, eax
|
||||
; =r (c) as c // output
|
||||
; r (a) as a // input
|
||||
r (b) as b
|
||||
}
|
||||
println('a: $a') // 100
|
||||
println('b: $b') // 20
|
||||
println('c: $c') // 120
|
||||
```
|
||||
|
||||
For more examples, see [github.com/vlang/v/tree/master/vlib/v/tests/assembly/asm_test.amd64.v](https://github.com/vlang/v/tree/master/vlib/v/tests/assembly/asm_test.amd64.v)
|
||||
|
||||
## Translating C to V
|
||||
|
||||
TODO: translating C to V will be available in V 0.3.
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
fn main() {
|
||||
a := 100
|
||||
b := 20
|
||||
mut c := 0
|
||||
asm amd64 {
|
||||
mov eax, a
|
||||
add eax, b
|
||||
mov c, eax
|
||||
; =r (c) // output
|
||||
; r (a) // input
|
||||
r (b)
|
||||
}
|
||||
println('a: $a') // 100
|
||||
println('b: $b') // 20
|
||||
println('c: $c') // 120
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
// This program displays the fibonacci sequence
|
||||
import os
|
||||
// import os
|
||||
|
||||
fn main() {
|
||||
// Check for user input
|
||||
if os.args.len != 2 {
|
||||
println('usage: fibonacci [rank]')
|
||||
//if os.args.len != 2 {
|
||||
// println('usage: fibonacci [rank]')
|
||||
|
||||
// Exit
|
||||
return
|
||||
}
|
||||
// return
|
||||
// }
|
||||
|
||||
// Parse first argument and cast it to int
|
||||
stop := os.args[1].int()
|
||||
|
||||
// stop := os.args[1].int()
|
||||
stop := 23
|
||||
// Can only calculate correctly until rank 92
|
||||
if stop > 92 {
|
||||
println('rank must be 92 or less')
|
||||
|
@ -20,10 +20,10 @@ fn main() {
|
|||
}
|
||||
|
||||
// Three consecutive terms of the sequence
|
||||
mut a := u64(0)
|
||||
mut b := u64(0)
|
||||
mut c := u64(1)
|
||||
|
||||
mut a := 0
|
||||
mut b := 0
|
||||
mut c := 1
|
||||
println(a+c+c)
|
||||
for _ in 0 .. stop {
|
||||
// Set a and b to the next term
|
||||
a = b
|
||||
|
|
|
@ -4,17 +4,17 @@ pub enum Linux_mem {
|
|||
page_size = 4096
|
||||
}
|
||||
|
||||
pub enum Wp_sys {
|
||||
wnohang = 0x00000001
|
||||
wuntraced = 0x00000002
|
||||
wstopped = 0x00000002
|
||||
wexited = 0x00000004
|
||||
wcontinued = 0x00000008
|
||||
wnowait = 0x01000000 // don't reap, just poll status.
|
||||
__wnothread = 0x20000000 // don't wait on children of other threads in this group
|
||||
__wall = 0x40000000 // wait on all children, regardless of type
|
||||
__wclone = 0x80000000 // wait only on non-sigchld children
|
||||
}
|
||||
pub const (
|
||||
wp_sys_wnohang = u64(0x00000001)
|
||||
wp_sys_wuntraced = u64(0x00000002)
|
||||
wp_sys_wstopped = u64(0x00000002)
|
||||
wp_sys_wexited = u64(0x00000004)
|
||||
wp_sys_wcontinued = u64(0x00000008)
|
||||
wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status.
|
||||
wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group
|
||||
wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type
|
||||
wp_sys___wclone = u64(0x80000000) // wait only on non-sigchld children
|
||||
)
|
||||
|
||||
// First argument to waitid:
|
||||
pub enum Wi_which {
|
||||
|
@ -32,7 +32,8 @@ pub enum Wi_si_code {
|
|||
cld_continued = 6 // stopped child has continued
|
||||
}
|
||||
|
||||
/* Paraphrased from "man 2 waitid" on Linux
|
||||
/*
|
||||
Paraphrased from "man 2 waitid" on Linux
|
||||
|
||||
Upon successful return, waitid() fills in the
|
||||
following fields of the siginfo_t structure
|
||||
|
@ -74,98 +75,96 @@ pub enum Sig_index {
|
|||
}
|
||||
|
||||
pub enum Signo {
|
||||
sighup = 1 // Hangup.
|
||||
sigint = 2 // Interactive attention signal.
|
||||
sigquit = 3 // Quit.
|
||||
sigill = 4 // Illegal instruction.
|
||||
sigtrap = 5 // Trace/breakpoint trap.
|
||||
sigabrt = 6 // Abnormal termination.
|
||||
sighup = 1 // Hangup.
|
||||
sigint = 2 // Interactive attention signal.
|
||||
sigquit = 3 // Quit.
|
||||
sigill = 4 // Illegal instruction.
|
||||
sigtrap = 5 // Trace/breakpoint trap.
|
||||
sigabrt = 6 // Abnormal termination.
|
||||
sigbus = 7
|
||||
sigfpe = 8 // Erroneous arithmetic operation.
|
||||
sigkill = 9 // Killed.
|
||||
sigfpe = 8 // Erroneous arithmetic operation.
|
||||
sigkill = 9 // Killed.
|
||||
sigusr1 = 10
|
||||
sigsegv = 11 // Invalid access to storage.
|
||||
sigsegv = 11 // Invalid access to storage.
|
||||
sigusr2 = 12
|
||||
sigpipe = 13 // Broken pipe.
|
||||
sigalrm = 14 // Alarm clock.
|
||||
sigterm = 15 // Termination request.
|
||||
sigpipe = 13 // Broken pipe.
|
||||
sigalrm = 14 // Alarm clock.
|
||||
sigterm = 15 // Termination request.
|
||||
sigstkflt = 16
|
||||
sigchld = 17
|
||||
sigcont = 18
|
||||
sigstop = 19
|
||||
sigtstp = 20
|
||||
sigttin = 21 // Background read from control terminal.
|
||||
sigttou = 22 // Background write to control terminal.
|
||||
sigttin = 21 // Background read from control terminal.
|
||||
sigttou = 22 // Background write to control terminal.
|
||||
sigurg = 23
|
||||
sigxcpu = 24 // CPU time limit exceeded.
|
||||
sigxfsz = 25 // File size limit exceeded.
|
||||
sigvtalrm = 26 // Virtual timer expired.
|
||||
sigprof = 27 // Profiling timer expired.
|
||||
sigxcpu = 24 // CPU time limit exceeded.
|
||||
sigxfsz = 25 // File size limit exceeded.
|
||||
sigvtalrm = 26 // Virtual timer expired.
|
||||
sigprof = 27 // Profiling timer expired.
|
||||
sigwinch = 28
|
||||
sigpoll = 29
|
||||
sigsys = 31
|
||||
}
|
||||
|
||||
|
||||
pub enum Fcntl {
|
||||
fd_cloexec = 0x00000001
|
||||
f_dupfd = 0x00000000
|
||||
f_exlck = 0x00000004
|
||||
f_getfd = 0x00000001
|
||||
f_getfl = 0x00000003
|
||||
f_getlk = 0x00000005
|
||||
f_getlk64 = 0x0000000c
|
||||
f_getown = 0x00000009
|
||||
f_getowner_uids = 0x00000011
|
||||
f_getown_ex = 0x00000010
|
||||
f_getsig = 0x0000000b
|
||||
f_ofd_getlk = 0x00000024
|
||||
f_ofd_setlk = 0x00000025
|
||||
f_ofd_setlkw = 0x00000026
|
||||
f_owner_pgrp = 0x00000002
|
||||
f_owner_pid = 0x00000001
|
||||
f_owner_tid = 0x00000000
|
||||
f_rdlck = 0x00000000
|
||||
f_setfd = 0x00000002
|
||||
f_setfl = 0x00000004
|
||||
f_setlk = 0x00000006
|
||||
f_setlk64 = 0x0000000d
|
||||
f_setlkw = 0x00000007
|
||||
f_setlkw64 = 0x0000000e
|
||||
f_setown = 0x00000008
|
||||
f_setown_ex = 0x0000000f
|
||||
f_setsig = 0x0000000a
|
||||
f_shlck = 0x00000008
|
||||
f_unlck = 0x00000002
|
||||
f_wrlck = 0x00000001
|
||||
lock_ex = 0x00000002
|
||||
lock_mand = 0x00000020
|
||||
lock_nb = 0x00000004
|
||||
lock_read = 0x00000040
|
||||
lock_rw = 0x000000c0
|
||||
lock_sh = 0x00000001
|
||||
lock_un = 0x00000008
|
||||
lock_write = 0x00000080
|
||||
o_accmode = 0x00000003
|
||||
o_append = 0x00000400
|
||||
o_cloexec = 0x00080000
|
||||
o_creat = 0x00000040
|
||||
o_direct = 0x00004000
|
||||
o_directory = 0x00010000
|
||||
o_dsync = 0x00001000
|
||||
o_excl = 0x00000080
|
||||
o_largefile = 0x00008000
|
||||
o_ndelay = 0x00000800
|
||||
o_noatime = 0x00040000
|
||||
o_noctty = 0x00000100
|
||||
o_nofollow = 0x00020000
|
||||
o_nonblock = 0x00000800
|
||||
o_path = 0x00200000
|
||||
o_rdonly = 0x00000000
|
||||
o_rdwr = 0x00000002
|
||||
o_trunc = 0x00000200
|
||||
o_wronly = 0x00000001
|
||||
}
|
||||
pub const (
|
||||
fcntlf_dupfd = 0x00000000
|
||||
fcntlf_exlck = 0x00000004
|
||||
fcntlf_getfd = 0x00000001
|
||||
fcntlf_getfl = 0x00000003
|
||||
fcntlf_getlk = 0x00000005
|
||||
fcntlf_getlk64 = 0x0000000c
|
||||
fcntlf_getown = 0x00000009
|
||||
fcntlf_getowner_uids = 0x00000011
|
||||
fcntlf_getown_ex = 0x00000010
|
||||
fcntlf_getsig = 0x0000000b
|
||||
fcntlf_ofd_getlk = 0x00000024
|
||||
fcntlf_ofd_setlk = 0x00000025
|
||||
fcntlf_ofd_setlkw = 0x00000026
|
||||
fcntlf_owner_pgrp = 0x00000002
|
||||
fcntlf_owner_pid = 0x00000001
|
||||
fcntlf_owner_tid = 0x00000000
|
||||
fcntlf_rdlck = 0x00000000
|
||||
fcntlf_setfd = 0x00000002
|
||||
fcntlf_setfl = 0x00000004
|
||||
fcntlf_setlk = 0x00000006
|
||||
fcntlf_setlk64 = 0x0000000d
|
||||
fcntlf_setlkw = 0x00000007
|
||||
fcntlf_setlkw64 = 0x0000000e
|
||||
fcntlf_setown = 0x00000008
|
||||
fcntlf_setown_ex = 0x0000000f
|
||||
fcntlf_setsig = 0x0000000a
|
||||
fcntlf_shlck = 0x00000008
|
||||
fcntlf_unlck = 0x00000002
|
||||
fcntlf_wrlck = 0x00000001
|
||||
fcntllock_ex = 0x00000002
|
||||
fcntllock_mand = 0x00000020
|
||||
fcntllock_nb = 0x00000004
|
||||
fcntllock_read = 0x00000040
|
||||
fcntllock_rw = 0x000000c0
|
||||
fcntllock_sh = 0x00000001
|
||||
fcntllock_un = 0x00000008
|
||||
fcntllock_write = 0x00000080
|
||||
fcntlo_accmode = 0x00000003
|
||||
fcntlo_append = 0x00000400
|
||||
fcntlo_cloexec = 0x00080000
|
||||
fcntlo_creat = 0x00000040
|
||||
fcntlo_direct = 0x00004000
|
||||
fcntlo_directory = 0x00010000
|
||||
fcntlo_dsync = 0x00001000
|
||||
fcntlo_excl = 0x00000080
|
||||
fcntlo_largefile = 0x00008000
|
||||
fcntlo_ndelay = 0x00000800
|
||||
fcntlo_noatime = 0x00040000
|
||||
fcntlo_noctty = 0x00000100
|
||||
fcntlo_nofollow = 0x00020000
|
||||
fcntlo_nonblock = 0x00000800
|
||||
fcntlo_path = 0x00200000
|
||||
fcntlo_rdonly = 0x00000000
|
||||
fcntlo_rdwr = 0x00000002
|
||||
fcntlo_trunc = 0x00000200
|
||||
fcntlo_wronly = 0x00000001
|
||||
)
|
||||
|
||||
pub enum Errno {
|
||||
enoerror = 0x00000000
|
||||
|
@ -222,104 +221,106 @@ pub enum Map_flags {
|
|||
map_fixed = 0x10
|
||||
map_file = 0x00
|
||||
map_anonymous = 0x20
|
||||
map_anon = 0x20
|
||||
map_huge_shift = 26
|
||||
map_huge_mask = 0x3f
|
||||
}
|
||||
|
||||
fn do_not_call_me_asm_keeper0() {
|
||||
unsafe {
|
||||
asm {
|
||||
"\n"
|
||||
"ret\n"
|
||||
""
|
||||
".intel_syntax noprefix\n"
|
||||
".globl _start, sys_call0\n"
|
||||
".globl sys_call1, sys_call2, sys_call3\n"
|
||||
".globl sys_call4, sys_call5, sys_call6\n"
|
||||
""
|
||||
"_start:\n"
|
||||
"xor rbp,rbp\n"
|
||||
"pop rdi\n"
|
||||
"mov rsi,rsp\n"
|
||||
"and rsp,-16\n"
|
||||
"call main\n"
|
||||
"mov rdi,rax\n" /* syscall param 1 = rax (ret value of main) */
|
||||
"mov rax,60\n" /* SYS_exit */
|
||||
"syscall\n"
|
||||
""
|
||||
// should never be reached, but if the OS somehow fails to kill us,
|
||||
// it will cause a segmentation fault
|
||||
"ret\n"
|
||||
"sys_call0:\n"
|
||||
"mov rax,rdi\n"
|
||||
"syscall\n"
|
||||
"ret\n"
|
||||
""
|
||||
"sys_call1:\n"
|
||||
"mov rax,rdi\n"
|
||||
"mov rdi,rsi\n"
|
||||
"syscall\n"
|
||||
"ret\n"
|
||||
""
|
||||
"sys_call2:\n"
|
||||
"mov rax,rdi\n"
|
||||
"mov rdi,rsi\n"
|
||||
"mov rsi,rdx\n"
|
||||
"syscall\n"
|
||||
"ret\n"
|
||||
""
|
||||
"sys_call3:\n"
|
||||
"mov rax,rdi\n"
|
||||
"mov rdi,rsi\n"
|
||||
"mov rsi,rdx\n"
|
||||
"mov rdx,rcx\n"
|
||||
"syscall\n"
|
||||
"ret\n"
|
||||
""
|
||||
"sys_call4:\n"
|
||||
"mov rax,rdi\n"
|
||||
"mov rdi,rsi\n"
|
||||
"mov rsi,rdx\n"
|
||||
"mov rdx,rcx\n"
|
||||
"mov r10,r8\n"
|
||||
"syscall\n"
|
||||
"ret\n"
|
||||
""
|
||||
"sys_call5:\n"
|
||||
"mov rax,rdi\n"
|
||||
"mov rdi,rsi\n"
|
||||
"mov rsi,rdx\n"
|
||||
"mov rdx,rcx\n"
|
||||
"mov r10,r8\n"
|
||||
"mov r8,r9\n"
|
||||
"syscall\n"
|
||||
"ret\n"
|
||||
""
|
||||
"sys_call6:\n"
|
||||
"mov rax,rdi\n"
|
||||
"mov rdi,rsi\n"
|
||||
"mov rsi,rdx\n"
|
||||
"mov rdx,rcx\n"
|
||||
"mov r10,r8\n"
|
||||
"mov r8,r9\n"
|
||||
"mov r9, [rsp+8]\n"
|
||||
"syscall\n"
|
||||
"ret\n"
|
||||
""
|
||||
".att_syntax \n"
|
||||
}
|
||||
fn sys_call0(scn u64) u64 {
|
||||
res := u64(0)
|
||||
asm amd64 {
|
||||
syscall
|
||||
; =a (res)
|
||||
; a (scn)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn sys_call0(scn u64) u64
|
||||
fn sys_call1(scn, arg1 u64) u64
|
||||
fn sys_call2(scn, arg1, arg2 u64) u64
|
||||
fn sys_call3(scn, arg1, arg2, arg3 u64) u64
|
||||
fn sys_call4(scn, arg1, arg2, arg3, arg4 u64) u64
|
||||
fn sys_call5(scn, arg1, arg2, arg3, arg4, arg5 u64) u64
|
||||
fn sys_call6(scn, arg1, arg2, arg3, arg4, arg5, arg6 u64) u64
|
||||
fn sys_call1(scn u64, arg1 u64) u64 {
|
||||
res := u64(0)
|
||||
asm amd64 {
|
||||
syscall
|
||||
; =a (res)
|
||||
; a (scn)
|
||||
D (arg1)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn sys_call2(scn u64, arg1 u64, arg2 u64) u64 {
|
||||
res := u64(0)
|
||||
asm amd64 {
|
||||
syscall
|
||||
; =a (res)
|
||||
; a (scn)
|
||||
D (arg1)
|
||||
S (arg2)
|
||||
}
|
||||
return res
|
||||
}
|
||||
fn sys_call3(scn u64, arg1 u64, arg2 u64, arg3 u64) u64 {
|
||||
res := u64(0)
|
||||
asm amd64 {
|
||||
syscall
|
||||
; =a (res)
|
||||
; a (scn)
|
||||
D (arg1)
|
||||
S (arg2)
|
||||
d (arg3)
|
||||
}
|
||||
return res
|
||||
}
|
||||
fn sys_call4(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64) u64 {
|
||||
res := u64(0)
|
||||
asm amd64 {
|
||||
mov r10, arg4
|
||||
syscall
|
||||
; =a (res)
|
||||
; a (scn)
|
||||
D (arg1)
|
||||
S (arg2)
|
||||
d (arg3)
|
||||
r (arg4)
|
||||
; r10
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn sys_call5(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64) u64 {
|
||||
res := u64(0)
|
||||
asm amd64 {
|
||||
mov r10, arg4
|
||||
mov r8, arg5
|
||||
syscall
|
||||
; =a (res)
|
||||
; a (scn)
|
||||
D (arg1)
|
||||
S (arg2)
|
||||
d (arg3)
|
||||
r (arg4)
|
||||
r (arg5)
|
||||
; r10 r8
|
||||
}
|
||||
return res
|
||||
}
|
||||
fn sys_call6(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64, arg6 u64) u64 {
|
||||
res := u64(0)
|
||||
asm amd64 {
|
||||
mov r10, arg4
|
||||
mov r8, arg5
|
||||
mov r9, arg6
|
||||
syscall
|
||||
; =a (res)
|
||||
; a (scn)
|
||||
D (arg1)
|
||||
S (arg2)
|
||||
d (arg3)
|
||||
r (arg4)
|
||||
r (arg5)
|
||||
r (arg6)
|
||||
; r10 r8 r9
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn split_int_errno(rc_in u64) (i64, Errno) {
|
||||
rc := i64(rc_in)
|
||||
|
@ -330,7 +331,7 @@ fn split_int_errno(rc_in u64) (i64, Errno) {
|
|||
}
|
||||
|
||||
// 0 sys_read unsigned int fd char *buf size_t count
|
||||
pub fn sys_read (fd i64, buf byteptr, count u64) (i64, Errno) {
|
||||
pub fn sys_read(fd i64, buf byteptr, count u64) (i64, Errno) {
|
||||
return split_int_errno(sys_call3(0, u64(fd), u64(buf), count))
|
||||
}
|
||||
|
||||
|
@ -339,8 +340,8 @@ pub fn sys_write(fd i64, buf byteptr, count u64) (i64, Errno) {
|
|||
return split_int_errno(sys_call3(1, u64(fd), u64(buf), count))
|
||||
}
|
||||
|
||||
pub fn sys_open(filename byteptr, flags Fcntl, mode int) (i64, Errno) {
|
||||
//2 sys_open const char *filename int flags int mode
|
||||
pub fn sys_open(filename byteptr, flags i64, mode int) (i64, Errno) {
|
||||
// 2 sys_open const char *filename int flags int mode
|
||||
return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(mode)))
|
||||
}
|
||||
|
||||
|
@ -392,19 +393,17 @@ pub fn sys_vfork() int {
|
|||
}
|
||||
|
||||
// 33 sys_dup2 unsigned int oldfd unsigned int newfd
|
||||
pub fn sys_dup2 (oldfd, newfd int) (i64, Errno) {
|
||||
return split_int_errno(sys_call2(33, u64(oldfd),u64(newfd)))
|
||||
pub fn sys_dup2(oldfd int, newfd int) (i64, Errno) {
|
||||
return split_int_errno(sys_call2(33, u64(oldfd), u64(newfd)))
|
||||
}
|
||||
|
||||
|
||||
//59 sys_execve const char *filename const char *const argv[] const char *const envp[]
|
||||
//pub fn sys_execve(filename byteptr, argv []byteptr, envp []byteptr) int {
|
||||
// 59 sys_execve const char *filename const char *const argv[] const char *const envp[]
|
||||
// pub fn sys_execve(filename byteptr, argv []byteptr, envp []byteptr) int {
|
||||
// return sys_call3(59, filename, argv, envp)
|
||||
//}
|
||||
|
||||
|
||||
// 60 sys_exit int error_code
|
||||
pub fn sys_exit (ec int) {
|
||||
pub fn sys_exit(ec int) {
|
||||
sys_call1(60, u64(ec))
|
||||
}
|
||||
|
||||
|
@ -414,12 +413,10 @@ pub fn sys_getuid() int {
|
|||
}
|
||||
|
||||
// 247 sys_waitid int which pid_t upid struct siginfo *infop int options struct rusage *ru
|
||||
pub fn sys_waitid (which Wi_which, pid int, infop &int, options Wp_sys, ru voidptr) Errno {
|
||||
pub fn sys_waitid(which Wi_which, pid int, infop &int, options int, ru voidptr) Errno {
|
||||
return Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru)))
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
A few years old, but still relevant
|
||||
https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
|
||||
|
|
283
vlib/v/ast/ast.v
283
vlib/v/ast/ast.v
|
@ -6,6 +6,7 @@ module ast
|
|||
import v.token
|
||||
import v.table
|
||||
import v.errors
|
||||
import v.pref
|
||||
|
||||
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
|
||||
|
||||
|
@ -17,14 +18,14 @@ pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr |
|
|||
RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
|
||||
StructInit | Type | TypeOf | UnsafeExpr
|
||||
|
||||
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
|
||||
EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
|
||||
GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return | SqlStmt |
|
||||
StructDecl | TypeDecl
|
||||
pub type Stmt = AsmStmt | AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl |
|
||||
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
|
||||
GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return |
|
||||
SqlStmt | StructDecl | TypeDecl
|
||||
|
||||
// NB: when you add a new Expr or Stmt type with a .pos field, remember to update
|
||||
// the .position() token.Position methods too.
|
||||
pub type ScopeObject = ConstField | GlobalField | Var
|
||||
pub type ScopeObject = AsmRegister | ConstField | GlobalField | Var
|
||||
|
||||
// TOOD: replace table.Param
|
||||
pub type Node = ConstField | EnumField | Expr | Field | File | GlobalField | IfBranch |
|
||||
|
@ -1032,6 +1033,175 @@ pub mut:
|
|||
in_prexpr bool // is the parent node an ast.PrefixExpr
|
||||
}
|
||||
|
||||
pub struct AsmStmt {
|
||||
pub:
|
||||
arch pref.Arch
|
||||
is_top_level bool
|
||||
is_volatile bool
|
||||
is_goto bool
|
||||
clobbered []AsmClobbered
|
||||
pos token.Position
|
||||
pub mut:
|
||||
templates []AsmTemplate
|
||||
scope &Scope
|
||||
output []AsmIO
|
||||
input []AsmIO
|
||||
global_labels []string // listed after clobbers, paired with is_goto == true
|
||||
local_labels []string // local to the assembly block
|
||||
exported_symbols []string // functions defined in assembly block, exported with `.globl`
|
||||
}
|
||||
|
||||
pub struct AsmTemplate {
|
||||
pub mut:
|
||||
name string
|
||||
is_label bool // `example_label:`
|
||||
is_directive bool // .globl assembly_function
|
||||
args []AsmArg
|
||||
comments []Comment
|
||||
pos token.Position
|
||||
}
|
||||
|
||||
// [eax+5] | j | eax | true | `a` | 0.594 | 123 | 'hi' | label_name
|
||||
pub type AsmArg = AsmAddressing | AsmAlias | AsmRegister | BoolLiteral | CharLiteral |
|
||||
FloatLiteral | IntegerLiteral | string
|
||||
|
||||
pub struct AsmRegister {
|
||||
pub:
|
||||
name string // eax or r12d
|
||||
mut:
|
||||
typ table.Type
|
||||
size int
|
||||
}
|
||||
|
||||
pub struct AsmAlias {
|
||||
pub:
|
||||
name string // a
|
||||
pos token.Position
|
||||
}
|
||||
|
||||
pub struct AsmAddressing {
|
||||
pub:
|
||||
displacement u32 // 8, 16 or 32 bit literal value
|
||||
scale int = -1 // 1, 2, 4, or 8 literal
|
||||
mode AddressingMode
|
||||
pos token.Position
|
||||
pub mut:
|
||||
base AsmArg // gpr
|
||||
index AsmArg // gpr
|
||||
}
|
||||
|
||||
// adressing modes:
|
||||
pub enum AddressingMode {
|
||||
invalid
|
||||
displacement // displacement
|
||||
base // base
|
||||
base_plus_displacement // base + displacement
|
||||
index_times_scale_plus_displacement // (index ∗ scale) + displacement
|
||||
base_plus_index_plus_displacement // base + (index ∗ scale) + displacement
|
||||
base_plus_index_times_scale_plus_displacement // base + index + displacement
|
||||
rip_plus_displacement // rip + displacement
|
||||
}
|
||||
|
||||
pub struct AsmClobbered {
|
||||
pub:
|
||||
reg AsmRegister
|
||||
pub mut:
|
||||
comments []Comment
|
||||
}
|
||||
|
||||
// : [alias_a] '=r' (a) // this is a comment
|
||||
pub struct AsmIO {
|
||||
pub:
|
||||
alias string // [alias_a]
|
||||
constraint string // '=r'
|
||||
expr Expr // (a)
|
||||
comments []Comment // // this is a comment
|
||||
typ table.Type
|
||||
pos token.Position
|
||||
}
|
||||
|
||||
pub const (
|
||||
// reference: https://en.wikipedia.org/wiki/X86#/media/File:Table_of_x86_Registers_svg.svg
|
||||
// map register size -> register name
|
||||
x86_no_number_register_list = map{
|
||||
8: ['al', 'ah', 'bl', 'bh', 'cl', 'ch', 'dl', 'dh', 'bpl', 'sil', 'dil', 'spl']
|
||||
16: ['ax', 'bx', 'cx', 'dx', 'bp', 'si', 'di', 'sp', /* segment registers */ 'cs', 'ss',
|
||||
'ds', 'es', 'fs', 'gs', 'flags', 'ip', /* task registers */ 'gdtr', 'idtr', 'tr', 'ldtr',
|
||||
// CSR register 'msw', /* FP core registers */ 'cw', 'sw', 'tw', 'fp_ip', 'fp_dp',
|
||||
'fp_cs', 'fp_ds', 'fp_opc']
|
||||
32: [
|
||||
'eax',
|
||||
'ebx',
|
||||
'ecx',
|
||||
'edx',
|
||||
'ebp',
|
||||
'esi',
|
||||
'edi',
|
||||
'esp',
|
||||
'eflags',
|
||||
'eip', /* CSR register */
|
||||
'mxcsr' /* 32-bit FP core registers 'fp_dp', 'fp_ip' (TODO: why are there duplicates?) */,
|
||||
]
|
||||
64: ['rax', 'rbx', 'rcx', 'rdx', 'rbp', 'rsi', 'rdi', 'rsp', 'rflags', 'rip']
|
||||
}
|
||||
// no comments because maps do not support comments
|
||||
// r#*: gp registers added in 64-bit extensions, can only be from 8-15 actually
|
||||
// *mm#: vector/simd registors
|
||||
// st#: floating point numbers
|
||||
// cr#: control/status registers
|
||||
// dr#: debug registers
|
||||
x86_with_number_register_list = map{
|
||||
8: map{
|
||||
'r#b': 16
|
||||
}
|
||||
16: map{
|
||||
'r#w': 16
|
||||
}
|
||||
32: map{
|
||||
'r#d': 16
|
||||
}
|
||||
64: map{
|
||||
'r#': 16
|
||||
'mm#': 16
|
||||
'cr#': 16
|
||||
'dr#': 16
|
||||
}
|
||||
80: map{
|
||||
'st#': 16
|
||||
}
|
||||
128: map{
|
||||
'xmm#': 32
|
||||
}
|
||||
256: map{
|
||||
'ymm#': 32
|
||||
}
|
||||
512: map{
|
||||
'zmm#': 32
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// TODO: saved priviled registers for arm
|
||||
const (
|
||||
arm_no_number_register_list = ['fp' /* aka r11 */, /* not instruction pointer: */ 'ip' /* aka r12 */,
|
||||
'sp' /* aka r13 */, 'lr' /* aka r14 */, /* this is instruction pointer ('program counter'): */
|
||||
'pc' /* aka r15 */,
|
||||
] // 'cpsr' and 'apsr' are special flags registers, but cannot be referred to directly
|
||||
arm_with_number_register_list = map{
|
||||
'r#': 16
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
riscv_no_number_register_list = ['zero', 'ra', 'sp', 'gp', 'tp']
|
||||
riscv_with_number_register_list = map{
|
||||
'x#': 32
|
||||
't#': 3
|
||||
's#': 12
|
||||
'a#': 8
|
||||
}
|
||||
)
|
||||
|
||||
pub struct AssertStmt {
|
||||
pub:
|
||||
pos token.Position
|
||||
|
@ -1383,7 +1553,17 @@ pub fn (node Node) position() token.Position {
|
|||
}
|
||||
ScopeObject {
|
||||
match node {
|
||||
ConstField, GlobalField, Var { return node.pos }
|
||||
ConstField, GlobalField, Var {
|
||||
return node.pos
|
||||
}
|
||||
AsmRegister {
|
||||
return token.Position{
|
||||
len: -1
|
||||
line_nr: -1
|
||||
pos: -1
|
||||
last_line: -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File {
|
||||
|
@ -1510,6 +1690,7 @@ pub fn (node Node) children() []Node {
|
|||
} else if node is ScopeObject {
|
||||
match node {
|
||||
GlobalField, ConstField, Var { children << node.expr }
|
||||
AsmRegister {}
|
||||
}
|
||||
} else {
|
||||
match node {
|
||||
|
@ -1559,3 +1740,93 @@ pub fn (mut lx IndexExpr) recursive_mapset_is_setter(val bool) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return all the registers for a give architecture
|
||||
pub fn all_registers(mut t table.Table, arch pref.Arch) map[string]ScopeObject {
|
||||
mut res := map[string]ScopeObject{}
|
||||
match arch {
|
||||
.amd64, .i386 {
|
||||
for bit_size, array in ast.x86_no_number_register_list {
|
||||
for name in array {
|
||||
res[name] = AsmRegister{
|
||||
name: name
|
||||
typ: t.bitsize_to_type(bit_size)
|
||||
size: bit_size
|
||||
}
|
||||
}
|
||||
}
|
||||
for bit_size, array in ast.x86_with_number_register_list {
|
||||
for name, max_num in array {
|
||||
for i in 0 .. max_num {
|
||||
hash_index := name.index('#') or {
|
||||
panic('ast.all_registers: no hashtag found')
|
||||
}
|
||||
assembled_name := '${name[..hash_index]}$i${name[hash_index + 1..]}'
|
||||
res[assembled_name] = AsmRegister{
|
||||
name: assembled_name
|
||||
typ: t.bitsize_to_type(bit_size)
|
||||
size: bit_size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.aarch32 {
|
||||
aarch32 := gen_all_registers(mut t, ast.arm_no_number_register_list, ast.arm_with_number_register_list,
|
||||
32)
|
||||
for k, v in aarch32 {
|
||||
res[k] = v
|
||||
}
|
||||
}
|
||||
.aarch64 {
|
||||
aarch64 := gen_all_registers(mut t, ast.arm_no_number_register_list, ast.arm_with_number_register_list,
|
||||
64)
|
||||
for k, v in aarch64 {
|
||||
res[k] = v
|
||||
}
|
||||
}
|
||||
.rv32 {
|
||||
rv32 := gen_all_registers(mut t, ast.riscv_no_number_register_list, ast.riscv_with_number_register_list,
|
||||
32)
|
||||
for k, v in rv32 {
|
||||
res[k] = v
|
||||
}
|
||||
}
|
||||
.rv64 {
|
||||
rv64 := gen_all_registers(mut t, ast.riscv_no_number_register_list, ast.riscv_with_number_register_list,
|
||||
64)
|
||||
for k, v in rv64 {
|
||||
res[k] = v
|
||||
}
|
||||
}
|
||||
else { // TODO
|
||||
panic('ast.all_registers: unhandled arch')
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// only for arm and riscv because x86 has different sized registers
|
||||
fn gen_all_registers(mut t table.Table, without_numbers []string, with_numbers map[string]int, bit_size int) map[string]ScopeObject {
|
||||
mut res := map[string]ScopeObject{}
|
||||
for name in without_numbers {
|
||||
res[name] = AsmRegister{
|
||||
name: name
|
||||
typ: t.bitsize_to_type(bit_size)
|
||||
size: bit_size
|
||||
}
|
||||
}
|
||||
for name, max_num in with_numbers {
|
||||
for i in 0 .. max_num {
|
||||
hash_index := name.index('#') or { panic('ast.all_registers: no hashtag found') }
|
||||
assembled_name := '${name[..hash_index]}$i${name[hash_index + 1..]}'
|
||||
res[assembled_name] = AsmRegister{
|
||||
name: assembled_name
|
||||
typ: t.bitsize_to_type(bit_size)
|
||||
size: bit_size
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -1907,7 +1907,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
// builtin C.m*, C.s* only - temp
|
||||
c.warn('function `$f.name` must be called from an `unsafe` block', call_expr.pos)
|
||||
}
|
||||
if f.mod != 'builtin' && f.language == .v && f.no_body && !c.pref.translated {
|
||||
if f.mod != 'builtin' && f.language == .v && f.no_body && !c.pref.translated && !f.is_unsafe {
|
||||
c.error('cannot call a function that does not have a body', call_expr.pos)
|
||||
}
|
||||
for generic_type in call_expr.generic_types {
|
||||
|
@ -3238,6 +3238,9 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
|||
}
|
||||
// c.expected_type = table.void_type
|
||||
match mut node {
|
||||
ast.AsmStmt {
|
||||
c.asm_stmt(mut node)
|
||||
}
|
||||
ast.AssertStmt {
|
||||
c.assert_stmt(node)
|
||||
}
|
||||
|
@ -3549,6 +3552,113 @@ fn (mut c Checker) go_stmt(mut node ast.GoStmt) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) {
|
||||
if stmt.is_goto {
|
||||
c.warn('inline assembly goto is not supported, it will most likely not work',
|
||||
stmt.pos)
|
||||
}
|
||||
if c.pref.backend == .js {
|
||||
c.error('inline assembly is not supported in js backend', stmt.pos)
|
||||
}
|
||||
if c.pref.backend == .c && c.pref.ccompiler_type == .msvc {
|
||||
c.error('msvc compiler does not support inline assembly', stmt.pos)
|
||||
}
|
||||
mut aliases := c.asm_ios(stmt.output, mut stmt.scope, true)
|
||||
aliases2 := c.asm_ios(stmt.input, mut stmt.scope, false)
|
||||
aliases << aliases2
|
||||
for template in stmt.templates {
|
||||
if template.is_directive {
|
||||
/*
|
||||
align n[,value]
|
||||
.skip n[,value]
|
||||
.space n[,value]
|
||||
.byte value1[,...]
|
||||
.word value1[,...]
|
||||
.short value1[,...]
|
||||
.int value1[,...]
|
||||
.long value1[,...]
|
||||
.quad immediate_value1[,...]
|
||||
.globl symbol
|
||||
.global symbol
|
||||
.section section
|
||||
.text
|
||||
.data
|
||||
.bss
|
||||
.fill repeat[,size[,value]]
|
||||
.org n
|
||||
.previous
|
||||
.string string[,...]
|
||||
.asciz string[,...]
|
||||
.ascii string[,...]
|
||||
*/
|
||||
if template.name !in ['skip', 'space', 'byte', 'word', 'short', 'int', 'long', 'quad',
|
||||
'globl', 'global', 'section', 'text', 'data', 'bss', 'fill', 'org', 'previous',
|
||||
'string', 'asciz', 'ascii'] { // all tcc supported assembler directive
|
||||
c.error('unknown assembler directive: `$template.name`', template.pos)
|
||||
}
|
||||
// if c.file in {
|
||||
|
||||
// }
|
||||
}
|
||||
for arg in template.args {
|
||||
c.asm_arg(arg, stmt, aliases)
|
||||
}
|
||||
}
|
||||
for clob in stmt.clobbered {
|
||||
c.asm_arg(clob.reg, stmt, aliases)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) {
|
||||
match mut arg {
|
||||
ast.AsmAlias {
|
||||
name := arg.name
|
||||
if name !in aliases && name !in stmt.local_labels && name !in stmt.global_labels {
|
||||
suggestion := util.new_suggestion(name, aliases)
|
||||
c.error(suggestion.say('alias or label `$arg.name` does not exist'), arg.pos)
|
||||
}
|
||||
}
|
||||
ast.AsmAddressing {
|
||||
if arg.scale !in [-1, 1, 2, 4, 8] {
|
||||
c.error('scale must be one of 1, 2, 4, or 8', arg.pos)
|
||||
}
|
||||
c.asm_arg(arg.base, stmt, aliases)
|
||||
c.asm_arg(arg.index, stmt, aliases)
|
||||
}
|
||||
ast.BoolLiteral {} // all of these are guarented to be correct.
|
||||
ast.FloatLiteral {}
|
||||
ast.CharLiteral {}
|
||||
ast.IntegerLiteral {}
|
||||
ast.AsmRegister {} // if the register is not found, the parser will register it as an alias
|
||||
string {}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) asm_ios(ios []ast.AsmIO, mut scope ast.Scope, output bool) []string {
|
||||
mut aliases := []string{}
|
||||
for io in ios {
|
||||
typ := c.expr(io.expr)
|
||||
if output {
|
||||
c.fail_if_immutable(io.expr)
|
||||
}
|
||||
if io.alias != '' {
|
||||
aliases << io.alias
|
||||
if io.alias in scope.objects {
|
||||
scope.objects[io.alias] = ast.Var{
|
||||
name: io.alias
|
||||
expr: io.expr
|
||||
is_arg: true
|
||||
typ: typ
|
||||
orig_type: typ
|
||||
pos: io.pos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
||||
if c.skip_flags {
|
||||
return
|
||||
|
@ -3998,6 +4108,23 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
|||
return table.void_type
|
||||
}
|
||||
|
||||
// pub fn (mut c Checker) asm_reg(mut node ast.AsmRegister) table.Type {
|
||||
// name := node.name
|
||||
|
||||
// for bit_size, array in ast.x86_no_number_register_list {
|
||||
// if name in array {
|
||||
// return c.table.bitsize_to_type(bit_size)
|
||||
// }
|
||||
// }
|
||||
// for bit_size, array in ast.x86_with_number_register_list {
|
||||
// if name in array {
|
||||
// return c.table.bitsize_to_type(bit_size)
|
||||
// }
|
||||
// }
|
||||
// c.error('invalid register name: `$name`', node.pos)
|
||||
// return table.void_type
|
||||
// }
|
||||
|
||||
pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) table.Type {
|
||||
node.expr_type = c.expr(node.expr) // type to be casted
|
||||
from_type_sym := c.table.get_type_symbol(node.expr_type)
|
||||
|
@ -5286,7 +5413,7 @@ fn (mut c Checker) find_obj_definition(obj ast.ScopeObject) ?ast.Expr {
|
|||
// TODO: remove once we have better type inference
|
||||
mut name := ''
|
||||
match obj {
|
||||
ast.Var, ast.ConstField, ast.GlobalField { name = obj.name }
|
||||
ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister { name = obj.name }
|
||||
}
|
||||
mut expr := ast.Expr{}
|
||||
if obj is ast.Var {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
vlib/v/checker/tests/asm_alias_does_not_exist.vv:2:11: error: alias or label `a` does not exist
|
||||
1 | asm amd64 {
|
||||
2 | mov ebx, a
|
||||
| ^
|
||||
3 | }
|
|
@ -0,0 +1,3 @@
|
|||
asm amd64 {
|
||||
mov ebx, a
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/asm_immutable_err.vv:9:9: error: `c` is immutable, declare it with `mut` to make it mutable
|
||||
7 | add eax, b
|
||||
8 | mov c, eax
|
||||
9 | ; =r (c) // output
|
||||
| ^
|
||||
10 | ; r (a) // input
|
||||
11 | r (b)
|
|
@ -0,0 +1,16 @@
|
|||
fn main() {
|
||||
a := 100
|
||||
b := 20
|
||||
c := 0
|
||||
asm amd64 {
|
||||
mov eax, a
|
||||
add eax, b
|
||||
mov c, eax
|
||||
; =r (c) // output
|
||||
; r (a) // input
|
||||
r (b)
|
||||
}
|
||||
println('a: $a') // 100
|
||||
println('b: $b') // 20
|
||||
println('c: $c') // 120
|
||||
}
|
|
@ -178,9 +178,11 @@ fn (mut tasks Tasks) run() {
|
|||
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv'
|
||||
}
|
||||
$if msvc {
|
||||
m_skip_files << 'vlib/v/checker/tests/asm_alias_does_not_exist.vv'
|
||||
m_skip_files << 'vlib/v/checker/tests/asm_immutable_err.vv'
|
||||
// TODO: investigate why MSVC regressed
|
||||
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_1.vv'
|
||||
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv'
|
||||
m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv'
|
||||
}
|
||||
for i in 0 .. tasks.all.len {
|
||||
if tasks.all[i].path in m_skip_files {
|
||||
|
|
152
vlib/v/fmt/fmt.v
152
vlib/v/fmt/fmt.v
|
@ -382,6 +382,9 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
|
|||
eprintln('stmt: ${node.pos:-42} | node: ${node.type_name():-20}')
|
||||
}
|
||||
match node {
|
||||
ast.AsmStmt {
|
||||
f.asm_stmt(node)
|
||||
}
|
||||
ast.AssignStmt {
|
||||
f.assign_stmt(node)
|
||||
}
|
||||
|
@ -462,6 +465,151 @@ pub fn (mut f Fmt) stmt(node ast.Stmt) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut f Fmt) asm_stmt(stmt ast.AsmStmt) {
|
||||
f.writeln('asm $stmt.arch {')
|
||||
f.indent++
|
||||
for template in stmt.templates {
|
||||
if template.is_directive {
|
||||
f.write('.')
|
||||
}
|
||||
f.write('$template.name')
|
||||
if template.is_label {
|
||||
f.write(':')
|
||||
} else {
|
||||
f.write(' ')
|
||||
}
|
||||
for i, arg in template.args {
|
||||
f.asm_arg(arg)
|
||||
if i + 1 < template.args.len {
|
||||
f.write(', ')
|
||||
}
|
||||
}
|
||||
if template.comments.len == 0 {
|
||||
f.writeln('')
|
||||
} else {
|
||||
f.comments(template.comments, inline: false)
|
||||
}
|
||||
}
|
||||
if stmt.output.len != 0 || stmt.input.len != 0 || stmt.clobbered.len != 0 {
|
||||
f.write('; ')
|
||||
}
|
||||
f.asm_ios(stmt.output)
|
||||
|
||||
if stmt.input.len != 0 || stmt.clobbered.len != 0 {
|
||||
f.write('; ')
|
||||
}
|
||||
f.asm_ios(stmt.input)
|
||||
|
||||
if stmt.clobbered.len != 0 {
|
||||
f.write('; ')
|
||||
}
|
||||
for i, clob in stmt.clobbered {
|
||||
if i != 0 {
|
||||
f.write(' ')
|
||||
}
|
||||
f.write(clob.reg.name)
|
||||
|
||||
if clob.comments.len == 0 {
|
||||
f.writeln('')
|
||||
} else {
|
||||
f.comments(clob.comments, inline: false)
|
||||
}
|
||||
}
|
||||
f.indent--
|
||||
f.writeln('}')
|
||||
}
|
||||
|
||||
fn (mut f Fmt) asm_arg(arg ast.AsmArg) {
|
||||
match arg {
|
||||
ast.AsmRegister {
|
||||
f.asm_reg(arg)
|
||||
}
|
||||
ast.AsmAlias {
|
||||
f.write('$arg.name')
|
||||
}
|
||||
ast.IntegerLiteral, ast.FloatLiteral, ast.CharLiteral {
|
||||
f.write(arg.val)
|
||||
}
|
||||
ast.BoolLiteral {
|
||||
f.write(arg.val.str())
|
||||
}
|
||||
string {
|
||||
f.write(arg)
|
||||
}
|
||||
ast.AsmAddressing {
|
||||
f.write('[')
|
||||
base := arg.base
|
||||
index := arg.index
|
||||
displacement := arg.displacement
|
||||
scale := arg.scale
|
||||
match arg.mode {
|
||||
.base {
|
||||
f.asm_arg(base)
|
||||
}
|
||||
.displacement {
|
||||
f.write('$displacement')
|
||||
}
|
||||
.base_plus_displacement {
|
||||
f.asm_arg(base)
|
||||
f.write(' + $displacement')
|
||||
}
|
||||
.index_times_scale_plus_displacement {
|
||||
f.asm_arg(index)
|
||||
f.write(' * $scale + $displacement')
|
||||
}
|
||||
.base_plus_index_plus_displacement {
|
||||
f.asm_arg(base)
|
||||
f.write(' + ')
|
||||
f.asm_arg(index)
|
||||
f.write(' + $displacement')
|
||||
}
|
||||
.base_plus_index_times_scale_plus_displacement {
|
||||
f.asm_arg(base)
|
||||
f.write(' + ')
|
||||
f.asm_arg(index)
|
||||
f.write(' * $scale + $displacement')
|
||||
}
|
||||
.rip_plus_displacement {
|
||||
f.asm_arg(base)
|
||||
f.write(' + $displacement')
|
||||
}
|
||||
.invalid {
|
||||
panic('fmt: invalid addressing mode')
|
||||
}
|
||||
}
|
||||
f.write(']')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut f Fmt) asm_reg(reg ast.AsmRegister) {
|
||||
f.write(reg.name)
|
||||
}
|
||||
|
||||
fn (mut f Fmt) asm_ios(ios []ast.AsmIO) {
|
||||
for i, io in ios {
|
||||
if i != 0 {
|
||||
f.write(' ')
|
||||
}
|
||||
|
||||
f.write('$io.constraint ($io.expr)')
|
||||
mut as_block := true
|
||||
if io.expr is ast.Ident {
|
||||
if io.expr.name == io.alias {
|
||||
as_block = false
|
||||
}
|
||||
}
|
||||
if as_block && io.alias != '' {
|
||||
f.write(' as $io.alias')
|
||||
}
|
||||
if io.comments.len == 0 {
|
||||
f.writeln('')
|
||||
} else {
|
||||
f.comments(io.comments, inline: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn stmt_is_single_line(stmt ast.Stmt) bool {
|
||||
return match stmt {
|
||||
ast.ExprStmt, ast.AssertStmt { expr_is_single_line(stmt.expr) }
|
||||
|
@ -734,8 +882,8 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
|||
f.write('none')
|
||||
}
|
||||
ast.OrExpr {
|
||||
// shouldn't happen, an or expression is always linked to a call expr
|
||||
panic('fmt: OrExpr should be linked to CallExpr')
|
||||
// shouldn't happen, an or expression is always linked to a call expr or index expr
|
||||
panic('fmt: OrExpr should be linked to ast.CallExpr or ast.IndexExpr')
|
||||
}
|
||||
ast.ParExpr {
|
||||
f.par_expr(node)
|
||||
|
|
|
@ -19,7 +19,9 @@ const (
|
|||
'delete', 'do', 'double', 'else', 'enum', 'error', 'exit', 'export', 'extern', 'float',
|
||||
'for', 'free', 'goto', 'if', 'inline', 'int', 'link', 'long', 'malloc', 'namespace', 'new',
|
||||
'panic', 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct',
|
||||
'switch', 'typedef', 'typename', 'union', 'unix', 'unsigned', 'void', 'volatile', 'while']
|
||||
'switch', 'typedef', 'typename', 'union', 'unix', 'unsigned', 'void', 'volatile', 'while',
|
||||
'template',
|
||||
]
|
||||
// same order as in token.Kind
|
||||
cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le']
|
||||
// when operands are switched
|
||||
|
@ -1001,6 +1003,10 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||
// println('g.stmt()')
|
||||
// g.writeln('//// stmt start')
|
||||
match node {
|
||||
ast.AsmStmt {
|
||||
g.write_v_source_line_info(node.pos)
|
||||
g.gen_asm_stmt(node)
|
||||
}
|
||||
ast.AssertStmt {
|
||||
g.write_v_source_line_info(node.pos)
|
||||
g.gen_assert_stmt(node)
|
||||
|
@ -1740,6 +1746,177 @@ fn (mut g Gen) gen_attrs(attrs []table.Attr) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_asm_stmt(stmt ast.AsmStmt) {
|
||||
g.write('__asm__')
|
||||
if stmt.is_volatile {
|
||||
g.write(' volatile')
|
||||
}
|
||||
if stmt.is_goto {
|
||||
g.write(' goto')
|
||||
}
|
||||
g.writeln(' (')
|
||||
g.indent++
|
||||
for mut template in stmt.templates {
|
||||
g.write('"')
|
||||
if template.is_directive {
|
||||
g.write('.')
|
||||
}
|
||||
g.write(template.name)
|
||||
if template.is_label {
|
||||
g.write(':')
|
||||
} else {
|
||||
g.write(' ')
|
||||
}
|
||||
// swap destionation and operands for att syntax
|
||||
if template.args.len != 0 {
|
||||
template.args.prepend(template.args[template.args.len - 1])
|
||||
template.args.delete(template.args.len - 1)
|
||||
}
|
||||
|
||||
for i, arg in template.args {
|
||||
g.asm_arg(arg, stmt)
|
||||
if i + 1 < template.args.len {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
|
||||
if !template.is_label {
|
||||
g.write(';')
|
||||
}
|
||||
g.writeln('"')
|
||||
}
|
||||
if !stmt.is_top_level {
|
||||
g.write(': ')
|
||||
}
|
||||
g.gen_asm_ios(stmt.output)
|
||||
if stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto {
|
||||
g.write(': ')
|
||||
}
|
||||
g.gen_asm_ios(stmt.input)
|
||||
if stmt.clobbered.len != 0 || stmt.is_goto {
|
||||
g.write(': ')
|
||||
}
|
||||
for i, clob in stmt.clobbered {
|
||||
g.write('"')
|
||||
g.write(clob.reg.name)
|
||||
g.write('"')
|
||||
if i + 1 < stmt.clobbered.len {
|
||||
g.writeln(',')
|
||||
} else {
|
||||
g.writeln('')
|
||||
}
|
||||
}
|
||||
if stmt.is_goto {
|
||||
g.write(': ')
|
||||
}
|
||||
for i, label in stmt.global_labels {
|
||||
g.write(label)
|
||||
if i + 1 < stmt.clobbered.len {
|
||||
g.writeln(',')
|
||||
} else {
|
||||
g.writeln('')
|
||||
}
|
||||
}
|
||||
g.indent--
|
||||
g.writeln(');')
|
||||
}
|
||||
|
||||
fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
|
||||
match arg {
|
||||
ast.AsmAlias {
|
||||
name := arg.name
|
||||
if name in stmt.local_labels || name in stmt.global_labels {
|
||||
g.write(if name in stmt.local_labels {
|
||||
name
|
||||
} else { // val in stmt.global_labels
|
||||
'%l[$name]'
|
||||
})
|
||||
} else {
|
||||
g.write('%[$name]')
|
||||
}
|
||||
}
|
||||
ast.CharLiteral {
|
||||
g.write("'$arg.val'")
|
||||
}
|
||||
ast.IntegerLiteral, ast.FloatLiteral {
|
||||
g.write('\$$arg.val')
|
||||
}
|
||||
ast.BoolLiteral {
|
||||
g.write('\$$arg.val.str()')
|
||||
}
|
||||
ast.AsmRegister {
|
||||
if !stmt.is_top_level {
|
||||
g.write('%') // escape percent in extended assembly
|
||||
}
|
||||
g.write('%$arg.name')
|
||||
}
|
||||
ast.AsmAddressing {
|
||||
base := arg.base
|
||||
index := arg.index
|
||||
displacement := arg.displacement
|
||||
scale := arg.scale
|
||||
match arg.mode {
|
||||
.base {
|
||||
g.write('(')
|
||||
g.asm_arg(base, stmt)
|
||||
}
|
||||
.displacement {
|
||||
g.write('${displacement}(')
|
||||
}
|
||||
.base_plus_displacement {
|
||||
g.write('${displacement}(')
|
||||
g.asm_arg(base, stmt)
|
||||
}
|
||||
.index_times_scale_plus_displacement {
|
||||
g.write('${displacement}(,')
|
||||
g.asm_arg(index, stmt)
|
||||
g.write(',')
|
||||
g.write(scale.str())
|
||||
}
|
||||
.base_plus_index_plus_displacement {
|
||||
g.write('${displacement}(')
|
||||
g.asm_arg(base, stmt)
|
||||
g.write(',')
|
||||
g.asm_arg(index, stmt)
|
||||
g.write(',1')
|
||||
}
|
||||
.base_plus_index_times_scale_plus_displacement {
|
||||
g.write('${displacement}(')
|
||||
g.asm_arg(base, stmt)
|
||||
g.write(',')
|
||||
g.asm_arg(index, stmt)
|
||||
g.write(',$scale')
|
||||
}
|
||||
.rip_plus_displacement {
|
||||
g.write('${displacement}(')
|
||||
g.asm_arg(base, stmt)
|
||||
}
|
||||
.invalid {
|
||||
g.error('invalid addressing mode', arg.pos)
|
||||
}
|
||||
}
|
||||
g.write(')')
|
||||
}
|
||||
string {
|
||||
g.write('$arg')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_asm_ios(ios []ast.AsmIO) {
|
||||
for i, io in ios {
|
||||
if io.alias != '' {
|
||||
g.write('[$io.alias] ')
|
||||
}
|
||||
g.write('"$io.constraint" ($io.expr)')
|
||||
if i + 1 < ios.len {
|
||||
g.writeln(',')
|
||||
} else {
|
||||
g.writeln('')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cnewlines(s string) string {
|
||||
return s.replace('\n', r'\n')
|
||||
}
|
||||
|
@ -3116,6 +3293,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||
return
|
||||
}
|
||||
left_type := g.unwrap_generic(node.left_type)
|
||||
// println('>>$node')
|
||||
left_sym := g.table.get_type_symbol(left_type)
|
||||
left_final_sym := g.table.get_final_type_symbol(left_type)
|
||||
unaliased_left := if left_sym.kind == .alias {
|
||||
|
|
|
@ -356,6 +356,9 @@ fn (mut g JsGen) stmts(stmts []ast.Stmt) {
|
|||
fn (mut g JsGen) stmt(node ast.Stmt) {
|
||||
g.stmt_start_pos = g.ns.out.len
|
||||
match node {
|
||||
ast.AsmStmt {
|
||||
panic('inline asm is not supported by js')
|
||||
}
|
||||
ast.AssertStmt {
|
||||
g.gen_assert_stmt(node)
|
||||
}
|
||||
|
|
|
@ -49,6 +49,10 @@ pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) {
|
|||
|
||||
pub fn (mut w Walker) stmt(node ast.Stmt) {
|
||||
match mut node {
|
||||
ast.AsmStmt {
|
||||
w.asm_io(node.output)
|
||||
w.asm_io(node.input)
|
||||
}
|
||||
ast.AssertStmt {
|
||||
w.expr(node.expr)
|
||||
w.n_asserts++
|
||||
|
@ -122,6 +126,12 @@ pub fn (mut w Walker) stmt(node ast.Stmt) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut w Walker) asm_io(ios []ast.AsmIO) {
|
||||
for io in ios {
|
||||
w.expr(io.expr)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut w Walker) defer_stmts(stmts []ast.DeferStmt) {
|
||||
for stmt in stmts {
|
||||
w.stmts(stmt.stmts)
|
||||
|
@ -145,15 +155,15 @@ fn (mut w Walker) expr(node ast.Expr) {
|
|||
ast.AnonFn {
|
||||
w.fn_decl(mut node.decl)
|
||||
}
|
||||
ast.Assoc {
|
||||
w.exprs(node.exprs)
|
||||
}
|
||||
ast.ArrayInit {
|
||||
w.expr(node.len_expr)
|
||||
w.expr(node.cap_expr)
|
||||
w.expr(node.default_expr)
|
||||
w.exprs(node.exprs)
|
||||
}
|
||||
ast.Assoc {
|
||||
w.exprs(node.exprs)
|
||||
}
|
||||
ast.ArrayDecompose {
|
||||
w.expr(node.expr)
|
||||
}
|
||||
|
|
|
@ -23,52 +23,55 @@ mut:
|
|||
file_base string // "hello.v"
|
||||
file_name string // "/home/user/hello.v"
|
||||
file_name_dir string // "/home/user"
|
||||
file_backend_mode table.Language // .c for .c.v|.c.vv|.c.vsh files; .js for .js.v files, .v otherwise.
|
||||
file_backend_mode table.Language // .c for .c.v|.c.vv|.c.vsh files; .js for .js.v files, .amd64/.rv32/other arches for .amd64.v/.rv32.v/etc. files, .v otherwise.
|
||||
scanner &scanner.Scanner
|
||||
comments_mode scanner.CommentsMode = .skip_comments
|
||||
// see comment in parse_file
|
||||
tok token.Token
|
||||
prev_tok token.Token
|
||||
peek_tok token.Token
|
||||
table &table.Table
|
||||
language table.Language
|
||||
inside_if bool
|
||||
inside_if_expr bool
|
||||
inside_ct_if_expr bool
|
||||
inside_or_expr bool
|
||||
inside_for bool
|
||||
inside_fn bool // true even with implicit main
|
||||
inside_unsafe_fn bool // true when in fn, marked with `[unsafe]`
|
||||
inside_str_interp bool
|
||||
or_is_handled bool // ignore `or` in this expression
|
||||
builtin_mod bool // are we in the `builtin` module?
|
||||
mod string // current module name
|
||||
is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree
|
||||
attrs []table.Attr // attributes before next decl stmt
|
||||
expr_mod string // for constructing full type names in parse_type()
|
||||
scope &ast.Scope
|
||||
global_scope &ast.Scope
|
||||
imports map[string]string // alias => mod_name
|
||||
ast_imports []ast.Import // mod_names
|
||||
used_imports []string // alias
|
||||
auto_imports []string // imports, the user does not need to specify
|
||||
imported_symbols map[string]string
|
||||
is_amp bool // for generating the right code for `&Foo{}`
|
||||
returns bool
|
||||
inside_match bool // to separate `match A { }` from `Struct{}`
|
||||
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
||||
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
||||
inside_match_body bool // to fix eval not used TODO
|
||||
inside_unsafe bool
|
||||
is_stmt_ident bool // true while the beginning of a statement is an ident/selector
|
||||
expecting_type bool // `is Type`, expecting type
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
vet_errors []vet.Error
|
||||
cur_fn_name string
|
||||
label_names []string
|
||||
in_generic_params bool // indicates if parsing between `<` and `>` of a method/function
|
||||
name_error bool // indicates if the token is not a name or the name is on another line
|
||||
tok token.Token
|
||||
prev_tok token.Token
|
||||
peek_tok token.Token
|
||||
table &table.Table
|
||||
language table.Language
|
||||
inside_if bool
|
||||
inside_if_expr bool
|
||||
inside_ct_if_expr bool
|
||||
inside_or_expr bool
|
||||
inside_for bool
|
||||
inside_fn bool // true even with implicit main
|
||||
inside_unsafe_fn bool
|
||||
inside_str_interp bool
|
||||
or_is_handled bool // ignore `or` in this expression
|
||||
builtin_mod bool // are we in the `builtin` module?
|
||||
mod string // current module name
|
||||
is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree
|
||||
attrs []table.Attr // attributes before next decl stmt
|
||||
expr_mod string // for constructing full type names in parse_type()
|
||||
scope &ast.Scope
|
||||
global_scope &ast.Scope
|
||||
imports map[string]string // alias => mod_name
|
||||
ast_imports []ast.Import // mod_names
|
||||
used_imports []string // alias
|
||||
auto_imports []string // imports, the user does not need to specify
|
||||
imported_symbols map[string]string
|
||||
is_amp bool // for generating the right code for `&Foo{}`
|
||||
returns bool
|
||||
inside_match bool // to separate `match A { }` from `Struct{}`
|
||||
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
||||
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
||||
inside_match_body bool // to fix eval not used TODO
|
||||
inside_unsafe bool
|
||||
is_stmt_ident bool // true while the beginning of a statement is an ident/selector
|
||||
expecting_type bool // `is Type`, expecting type
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
vet_errors []vet.Error
|
||||
cur_fn_name string
|
||||
label_names []string
|
||||
in_generic_params bool // indicates if parsing between `<` and `>` of a method/function
|
||||
name_error bool // indicates if the token is not a name or the name is on another line
|
||||
n_asm int // controls assembly labels
|
||||
inside_asm_template bool
|
||||
inside_asm bool
|
||||
}
|
||||
|
||||
// for tests
|
||||
|
@ -134,14 +137,28 @@ pub fn (mut p Parser) set_path(path string) {
|
|||
p.file_name = path
|
||||
p.file_base = os.base(path)
|
||||
p.file_name_dir = os.dir(path)
|
||||
if path.ends_with('_c.v') || path.ends_with('.c.v') || path.ends_with('.c.vv')
|
||||
|| path.ends_with('.c.vsh') {
|
||||
p.file_backend_mode = .c
|
||||
} else if path.ends_with('_js.v') || path.ends_with('.js.v') || path.ends_with('.js.vv')
|
||||
|| path.ends_with('.js.vsh') {
|
||||
p.file_backend_mode = .js
|
||||
} else {
|
||||
before_dot_v := path.before('.v') // also works for .vv and .vsh
|
||||
language := before_dot_v.all_after_last('.')
|
||||
langauge_with_underscore := before_dot_v.all_after_last('_')
|
||||
if language == before_dot_v && langauge_with_underscore == before_dot_v {
|
||||
p.file_backend_mode = .v
|
||||
return
|
||||
}
|
||||
actual_language := if language == before_dot_v { langauge_with_underscore } else { language }
|
||||
match actual_language {
|
||||
'c' {
|
||||
p.file_backend_mode = .c
|
||||
}
|
||||
'js' {
|
||||
p.file_backend_mode = .js
|
||||
}
|
||||
else {
|
||||
arch := pref.arch_from_string(actual_language) or { pref.Arch._auto }
|
||||
p.file_backend_mode = table.pref_arch_to_table_language(arch)
|
||||
if arch == ._auto {
|
||||
p.file_backend_mode = .v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,7 +362,7 @@ pub fn (mut p Parser) init_parse_fns() {
|
|||
}
|
||||
|
||||
pub fn (mut p Parser) read_first_token() {
|
||||
// need to call next() 4 times to get peek token 1,2,3 and current token
|
||||
// need to call next() 2 times to get peek token and current token
|
||||
p.next()
|
||||
p.next()
|
||||
}
|
||||
|
@ -430,7 +447,8 @@ fn (mut p Parser) check(expected token.Kind) {
|
|||
// for p.tok.kind in [.line_comment, .mline_comment] {
|
||||
// p.next()
|
||||
// }
|
||||
if p.tok.kind == expected {
|
||||
|
||||
if _likely_(p.tok.kind == expected) {
|
||||
p.next()
|
||||
} else {
|
||||
if expected == .name {
|
||||
|
@ -507,6 +525,9 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
|
|||
p.attributes()
|
||||
continue
|
||||
}
|
||||
.key_asm {
|
||||
return p.asm_stmt(true)
|
||||
}
|
||||
.key_interface {
|
||||
return p.interface_decl()
|
||||
}
|
||||
|
@ -802,6 +823,9 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
|||
p.tok.position())
|
||||
return ast.Stmt{}
|
||||
}
|
||||
.key_asm {
|
||||
return p.asm_stmt(false)
|
||||
}
|
||||
// literals, 'if', etc. in here
|
||||
else {
|
||||
return p.parse_multi_expr(is_top_level)
|
||||
|
@ -809,6 +833,523 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
|
||||
p.inside_asm = true
|
||||
p.inside_asm_template = true
|
||||
defer {
|
||||
p.inside_asm = false
|
||||
p.inside_asm_template = false
|
||||
}
|
||||
p.n_asm = 0
|
||||
if is_top_level {
|
||||
p.top_level_statement_start()
|
||||
}
|
||||
mut backup_scope := p.scope
|
||||
|
||||
pos := p.tok.position()
|
||||
|
||||
p.check(.key_asm)
|
||||
mut arch := pref.arch_from_string(p.tok.lit) or { pref.Arch._auto }
|
||||
mut is_volatile := false
|
||||
mut is_goto := false
|
||||
if p.tok.lit == 'volatile' && p.tok.kind == .name {
|
||||
arch = pref.arch_from_string(p.peek_tok.lit) or { pref.Arch._auto }
|
||||
is_volatile = true
|
||||
p.check(.name)
|
||||
} else if p.tok.kind == .key_goto {
|
||||
arch = pref.arch_from_string(p.peek_tok.lit) or { pref.Arch._auto }
|
||||
is_goto = true
|
||||
p.check(.key_goto)
|
||||
}
|
||||
if arch == ._auto && !p.pref.is_fmt {
|
||||
p.error('unknown assembly architecture')
|
||||
}
|
||||
if p.tok.kind != .name {
|
||||
p.error('must specify assembly architecture')
|
||||
} else {
|
||||
p.check(.name)
|
||||
}
|
||||
|
||||
p.check_for_impure_v(table.pref_arch_to_table_language(arch), p.prev_tok.position())
|
||||
|
||||
p.check(.lcbr)
|
||||
p.scope = &ast.Scope{
|
||||
parent: 0 // you shouldn't be able to reference other variables in assembly blocks
|
||||
detached_from_parent: true
|
||||
start_pos: p.tok.pos
|
||||
objects: ast.all_registers(mut p.table, arch) //
|
||||
}
|
||||
|
||||
mut local_labels := []string{}
|
||||
mut exported_symbols := []string{}
|
||||
// riscv: https://github.com/jameslzhu/riscv-card/blob/master/riscv-card.pdf
|
||||
// x86: https://www.felixcloutier.com/x86/
|
||||
// arm: https://developer.arm.com/documentation/dui0068/b/arm-instruction-reference
|
||||
mut templates := []ast.AsmTemplate{}
|
||||
for p.tok.kind !in [.semicolon, .rcbr] {
|
||||
template_pos := p.tok.position()
|
||||
mut name := ''
|
||||
is_directive := p.tok.kind == .dot
|
||||
if is_directive {
|
||||
p.check(.dot)
|
||||
}
|
||||
if p.tok.kind in [.key_in, .key_lock, .key_orelse] { // `in`, `lock`, `or` are v keywords that are also x86/arm/riscv instructions.
|
||||
name = p.tok.kind.str()
|
||||
p.next()
|
||||
} else {
|
||||
name = p.tok.lit
|
||||
p.check(.name)
|
||||
}
|
||||
// dots are part of instructions for some riscv extensions
|
||||
if arch in [.rv32, .rv64] {
|
||||
for p.tok.kind == .dot {
|
||||
name += '.'
|
||||
p.check(.dot)
|
||||
name += p.tok.lit
|
||||
p.check(.name)
|
||||
}
|
||||
}
|
||||
mut is_label := false
|
||||
|
||||
mut args := []ast.AsmArg{}
|
||||
args_loop: for {
|
||||
match p.tok.kind {
|
||||
.name {
|
||||
args << p.reg_or_alias()
|
||||
}
|
||||
.number {
|
||||
number_lit := p.parse_number_literal()
|
||||
match number_lit {
|
||||
ast.FloatLiteral {
|
||||
args << ast.FloatLiteral{
|
||||
...number_lit
|
||||
}
|
||||
}
|
||||
ast.IntegerLiteral {
|
||||
args << ast.IntegerLiteral{
|
||||
...number_lit
|
||||
}
|
||||
}
|
||||
else {
|
||||
verror('p.parse_number_literal() invalid output: `$number_lit`')
|
||||
}
|
||||
}
|
||||
}
|
||||
.chartoken {
|
||||
args << ast.CharLiteral{
|
||||
val: p.tok.lit
|
||||
pos: p.tok.position()
|
||||
}
|
||||
p.check(.chartoken)
|
||||
}
|
||||
.colon {
|
||||
is_label = true
|
||||
p.check(.colon)
|
||||
local_labels << name
|
||||
break
|
||||
}
|
||||
.lsbr {
|
||||
args << p.asm_addressing()
|
||||
}
|
||||
.rcbr {
|
||||
break
|
||||
}
|
||||
.semicolon {
|
||||
break
|
||||
}
|
||||
else {
|
||||
p.error('invalid token in assembly block')
|
||||
}
|
||||
}
|
||||
if p.tok.kind == .comma {
|
||||
p.check(.comma)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
mut comments := []ast.Comment{}
|
||||
for p.tok.kind == .comment {
|
||||
comments << p.comment()
|
||||
}
|
||||
if is_directive && name in ['globl', 'global'] {
|
||||
exported_symbols << args
|
||||
}
|
||||
templates << ast.AsmTemplate{
|
||||
name: name
|
||||
args: args
|
||||
comments: comments
|
||||
is_label: is_label
|
||||
is_directive: is_directive
|
||||
pos: template_pos.extend(p.tok.position())
|
||||
}
|
||||
}
|
||||
mut scope := p.scope
|
||||
p.scope = backup_scope
|
||||
p.inside_asm_template = false
|
||||
mut output, mut input, mut clobbered, mut global_labels := []ast.AsmIO{}, []ast.AsmIO{}, []ast.AsmClobbered{}, []string{}
|
||||
if !is_top_level {
|
||||
if p.tok.kind == .semicolon {
|
||||
output = p.asm_ios(true)
|
||||
if p.tok.kind == .semicolon {
|
||||
input = p.asm_ios(false)
|
||||
}
|
||||
if p.tok.kind == .semicolon {
|
||||
// because p.reg_or_alias() requires the scope with registers to recognize registers.
|
||||
backup_scope = p.scope
|
||||
p.scope = scope
|
||||
p.check(.semicolon)
|
||||
for p.tok.kind == .name {
|
||||
reg := p.reg_or_alias()
|
||||
|
||||
mut comments := []ast.Comment{}
|
||||
for p.tok.kind == .comment {
|
||||
comments << p.comment()
|
||||
}
|
||||
if reg is ast.AsmRegister {
|
||||
clobbered << ast.AsmClobbered{
|
||||
reg: reg
|
||||
comments: comments
|
||||
}
|
||||
} else {
|
||||
p.error('not a register: $reg')
|
||||
}
|
||||
if p.tok.kind in [.rcbr, .semicolon] {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if is_goto && p.tok.kind == .semicolon {
|
||||
p.check(.semicolon)
|
||||
for p.tok.kind == .name {
|
||||
global_labels << p.tok.lit
|
||||
p.check(.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if p.tok.kind == .semicolon {
|
||||
p.error('extended assembly is not allowed as a top level statement')
|
||||
}
|
||||
p.scope = backup_scope
|
||||
p.check(.rcbr)
|
||||
if is_top_level {
|
||||
p.top_level_statement_end()
|
||||
}
|
||||
scope.end_pos = p.prev_tok.pos
|
||||
|
||||
return ast.AsmStmt{
|
||||
arch: arch
|
||||
is_goto: is_goto
|
||||
is_volatile: is_volatile
|
||||
templates: templates
|
||||
output: output
|
||||
input: input
|
||||
clobbered: clobbered
|
||||
pos: pos.extend(p.tok.position())
|
||||
is_top_level: is_top_level
|
||||
scope: scope
|
||||
global_labels: global_labels
|
||||
local_labels: local_labels
|
||||
exported_symbols: exported_symbols
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut p Parser) reg_or_alias() ast.AsmArg {
|
||||
assert p.tok.kind == .name
|
||||
if p.tok.lit in p.scope.objects {
|
||||
x := p.scope.objects[p.tok.lit]
|
||||
if x is ast.AsmRegister {
|
||||
b := x
|
||||
p.check(.name)
|
||||
return b
|
||||
} else {
|
||||
panic('parser bug: non-register ast.ScopeObject found in scope')
|
||||
}
|
||||
} else {
|
||||
p.check(.name)
|
||||
return ast.AsmAlias{
|
||||
name: p.prev_tok.lit
|
||||
pos: p.prev_tok.position()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn (mut p Parser) asm_addressing() ast.AsmAddressing {
|
||||
// pos := p.tok.position()
|
||||
// p.check(.lsbr)
|
||||
// unknown_addressing_mode := 'unknown addressing mode. supported ones are [displacement], [base], [base + displacement] [index ∗ scale + displacement], [base + index ∗ scale + displacement], [base + index + displacement] [rip + displacement]'
|
||||
// mut mode := ast.AddressingMode.invalid
|
||||
// if p.peek_tok.kind == .rsbr {
|
||||
// if p.tok.kind == .name {
|
||||
// mode = .base
|
||||
// } else if p.tok.kind == .number {
|
||||
// mode = .displacement
|
||||
// } else {
|
||||
// p.error(unknown_addressing_mode)
|
||||
// }
|
||||
// } else if p.peek_tok.kind == .mul {
|
||||
// mode = .index_times_scale_plus_displacement
|
||||
// } else if p.tok.lit == 'rip' {
|
||||
// mode = .rip_plus_displacement
|
||||
// } else if p.peek_tok3.kind == .mul {
|
||||
// mode = .base_plus_index_times_scale_plus_displacement
|
||||
// } else if p.peek_tok.kind == .plus && p.peek_tok3.kind == .rsbr {
|
||||
// mode = .base_plus_displacement
|
||||
// } else if p.peek_tok.kind == .plus && p.peek_tok3.kind == .plus {
|
||||
// mode = .base_plus_index_plus_displacement
|
||||
// } else {
|
||||
// p.error(unknown_addressing_mode)
|
||||
// }
|
||||
// mut displacement, mut base, mut index, mut scale := u32(0), ast.AsmArg{}, ast.AsmArg{}, -1
|
||||
|
||||
// match mode {
|
||||
// .base {
|
||||
// base = p.reg_or_alias()
|
||||
// }
|
||||
// .displacement {
|
||||
// displacement = p.tok.lit.u32()
|
||||
// p.check(.number)
|
||||
// }
|
||||
// .base_plus_displacement {
|
||||
// base = p.reg_or_alias()
|
||||
// p.check(.plus)
|
||||
// displacement = p.tok.lit.u32()
|
||||
// p.check(.number)
|
||||
// }
|
||||
// .index_times_scale_plus_displacement {
|
||||
// index = p.reg_or_alias()
|
||||
// p.check(.mul)
|
||||
// scale = p.tok.lit.int()
|
||||
// p.check(.number)
|
||||
// p.check(.plus)
|
||||
// displacement = p.tok.lit.u32()
|
||||
// p.check(.number)
|
||||
// }
|
||||
// .base_plus_index_times_scale_plus_displacement {
|
||||
// base = p.reg_or_alias()
|
||||
// p.check(.plus)
|
||||
// index = p.reg_or_alias()
|
||||
// p.check(.mul)
|
||||
// scale = p.tok.lit.int()
|
||||
// p.check(.number)
|
||||
// p.check(.plus)
|
||||
// displacement = p.tok.lit.u32()
|
||||
// p.check(.number)
|
||||
// }
|
||||
// .rip_plus_displacement {
|
||||
// base = p.reg_or_alias()
|
||||
// p.check(.plus)
|
||||
// displacement = p.tok.lit.u32()
|
||||
// p.check(.number)
|
||||
// }
|
||||
// .base_plus_index_plus_displacement {
|
||||
// base = p.reg_or_alias()
|
||||
// p.check(.plus)
|
||||
// index = p.reg_or_alias()
|
||||
// p.check(.plus)
|
||||
// displacement = p.tok.lit.u32()
|
||||
// p.check(.number)
|
||||
// }
|
||||
// .invalid {} // there was already an error above
|
||||
// }
|
||||
|
||||
// p.check(.rsbr)
|
||||
// return ast.AsmAddressing{
|
||||
// base: base
|
||||
// displacement: displacement
|
||||
// index: index
|
||||
// scale: scale
|
||||
// mode: mode
|
||||
// pos: pos.extend(p.prev_tok.position())
|
||||
// }
|
||||
// }
|
||||
fn (mut p Parser) asm_addressing() ast.AsmAddressing {
|
||||
pos := p.tok.position()
|
||||
p.check(.lsbr)
|
||||
unknown_addressing_mode := 'unknown addressing mode. supported ones are [displacement], [base], [base + displacement], [index ∗ scale + displacement], [base + index ∗ scale + displacement], [base + index + displacement], [rip + displacement]'
|
||||
// this mess used to look much cleaner before the removal of peek_tok3, see above
|
||||
if p.peek_tok.kind == .rsbr { // [displacement] or [base]
|
||||
if p.tok.kind == .name {
|
||||
base := p.reg_or_alias()
|
||||
p.check(.rsbr)
|
||||
return ast.AsmAddressing{
|
||||
mode: .base
|
||||
base: base
|
||||
pos: pos.extend(p.prev_tok.position())
|
||||
}
|
||||
} else if p.tok.kind == .number {
|
||||
displacement := p.tok.lit.u32()
|
||||
p.check(.name)
|
||||
p.check(.rsbr)
|
||||
return ast.AsmAddressing{
|
||||
mode: .displacement
|
||||
displacement: displacement
|
||||
pos: pos.extend(p.prev_tok.position())
|
||||
}
|
||||
} else {
|
||||
p.error(unknown_addressing_mode)
|
||||
}
|
||||
}
|
||||
if p.peek_tok.kind == .plus && p.tok.kind == .name { // [base + displacement], [base + index ∗ scale + displacement], [base + index + displacement] or [rip + displacement]
|
||||
if p.tok.lit == 'rip' {
|
||||
p.check(.name)
|
||||
p.check(.plus)
|
||||
displacement := p.tok.lit.u32()
|
||||
p.check(.number)
|
||||
return ast.AsmAddressing{
|
||||
mode: .rip_plus_displacement
|
||||
base: 'rip'
|
||||
displacement: displacement
|
||||
pos: pos.extend(p.prev_tok.position())
|
||||
}
|
||||
}
|
||||
base := p.reg_or_alias()
|
||||
p.check(.plus)
|
||||
if p.peek_tok.kind == .rsbr {
|
||||
if p.tok.kind == .number {
|
||||
displacement := p.tok.lit.u32()
|
||||
p.check(.number)
|
||||
p.check(.rsbr)
|
||||
return ast.AsmAddressing{
|
||||
mode: .base_plus_displacement
|
||||
base: base
|
||||
displacement: displacement
|
||||
pos: pos.extend(p.prev_tok.position())
|
||||
}
|
||||
} else {
|
||||
p.error(unknown_addressing_mode)
|
||||
}
|
||||
}
|
||||
index := p.reg_or_alias()
|
||||
if p.tok.kind == .mul {
|
||||
p.check(.mul)
|
||||
scale := p.tok.lit.int()
|
||||
p.check(.number)
|
||||
p.check(.plus)
|
||||
displacement := p.tok.lit.u32()
|
||||
p.check(.number)
|
||||
p.check(.rsbr)
|
||||
return ast.AsmAddressing{
|
||||
mode: .base_plus_index_times_scale_plus_displacement
|
||||
base: base
|
||||
index: index
|
||||
scale: scale
|
||||
displacement: displacement
|
||||
pos: pos.extend(p.prev_tok.position())
|
||||
}
|
||||
} else if p.tok.kind == .plus {
|
||||
p.check(.plus)
|
||||
displacement := p.tok.lit.u32()
|
||||
p.check(.number)
|
||||
p.check(.rsbr)
|
||||
return ast.AsmAddressing{
|
||||
mode: .base_plus_index_plus_displacement
|
||||
base: base
|
||||
index: index
|
||||
displacement: displacement
|
||||
pos: pos.extend(p.prev_tok.position())
|
||||
}
|
||||
}
|
||||
}
|
||||
if p.peek_tok.kind == .mul { // [index ∗ scale + displacement]
|
||||
index := p.reg_or_alias()
|
||||
p.check(.mul)
|
||||
scale := p.tok.lit.int()
|
||||
p.check(.number)
|
||||
p.check(.plus)
|
||||
displacement := p.tok.lit.u32()
|
||||
p.check(.number)
|
||||
p.check(.rsbr)
|
||||
return ast.AsmAddressing{
|
||||
mode: .index_times_scale_plus_displacement
|
||||
index: index
|
||||
scale: scale
|
||||
displacement: displacement
|
||||
pos: pos.extend(p.prev_tok.position())
|
||||
}
|
||||
}
|
||||
p.error(unknown_addressing_mode)
|
||||
return ast.AsmAddressing{}
|
||||
}
|
||||
|
||||
fn (mut p Parser) asm_ios(output bool) []ast.AsmIO {
|
||||
mut res := []ast.AsmIO{}
|
||||
p.check(.semicolon)
|
||||
if p.tok.kind in [.rcbr, .semicolon] {
|
||||
return []
|
||||
}
|
||||
for {
|
||||
pos := p.tok.position()
|
||||
|
||||
mut constraint := ''
|
||||
if p.tok.kind == .lpar {
|
||||
constraint = if output { '+r' } else { 'r' } // default constraint
|
||||
} else {
|
||||
constraint += match p.tok.kind {
|
||||
.assign {
|
||||
'='
|
||||
}
|
||||
.plus {
|
||||
'+'
|
||||
}
|
||||
.mod {
|
||||
'%'
|
||||
}
|
||||
.amp {
|
||||
'&'
|
||||
}
|
||||
else {
|
||||
''
|
||||
}
|
||||
}
|
||||
if constraint != '' {
|
||||
p.next()
|
||||
}
|
||||
if p.tok.kind == .assign {
|
||||
constraint += '='
|
||||
p.check(.assign)
|
||||
} else if p.tok.kind == .plus {
|
||||
constraint += '+'
|
||||
p.check(.plus)
|
||||
}
|
||||
constraint += p.tok.lit
|
||||
p.check(.name)
|
||||
}
|
||||
mut expr := p.expr(0)
|
||||
if mut expr is ast.ParExpr {
|
||||
expr = expr.expr
|
||||
} else {
|
||||
p.error('asm in/output must be incolsed in brackets $expr.type_name()')
|
||||
}
|
||||
mut alias := ''
|
||||
if p.tok.kind == .key_as {
|
||||
p.check(.key_as)
|
||||
alias = p.tok.lit
|
||||
p.check(.name)
|
||||
} else if mut expr is ast.Ident {
|
||||
alias = expr.name
|
||||
}
|
||||
// for constraints like `a`, no alias is needed, it is reffered to as rcx
|
||||
mut comments := []ast.Comment{}
|
||||
for p.tok.kind == .comment {
|
||||
comments << p.comment()
|
||||
}
|
||||
|
||||
res << ast.AsmIO{
|
||||
alias: alias
|
||||
constraint: constraint
|
||||
expr: expr
|
||||
comments: comments
|
||||
pos: pos.extend(p.prev_tok.position())
|
||||
}
|
||||
p.n_asm++
|
||||
if p.tok.kind in [.semicolon, .rcbr] {
|
||||
break
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn (mut p Parser) expr_list() ([]ast.Expr, []ast.Comment) {
|
||||
mut exprs := []ast.Expr{}
|
||||
mut comments := []ast.Comment{}
|
||||
|
|
|
@ -322,7 +322,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
}
|
||||
}
|
||||
else {
|
||||
if p.tok.kind != .eof {
|
||||
if p.tok.kind != .eof && !(p.tok.kind == .rsbr && p.inside_asm) {
|
||||
// eof should be handled where it happens
|
||||
p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.position())
|
||||
return ast.Expr{}
|
||||
|
@ -360,13 +360,17 @@ pub fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_iden
|
|||
}
|
||||
} else if p.tok.kind == .key_as {
|
||||
// sum type as cast `x := SumType as Variant`
|
||||
pos := p.tok.position()
|
||||
p.next()
|
||||
typ := p.parse_type()
|
||||
node = ast.AsCast{
|
||||
expr: node
|
||||
typ: typ
|
||||
pos: pos
|
||||
if !p.inside_asm {
|
||||
pos := p.tok.position()
|
||||
p.next()
|
||||
typ := p.parse_type()
|
||||
node = ast.AsCast{
|
||||
expr: node
|
||||
typ: typ
|
||||
pos: pos
|
||||
}
|
||||
} else {
|
||||
return node
|
||||
}
|
||||
} else if p.tok.kind == .left_shift && p.is_stmt_ident {
|
||||
// arr << elem
|
||||
|
|
|
@ -6,6 +6,7 @@ import v.gen.c
|
|||
import v.table
|
||||
import v.checker
|
||||
import v.pref
|
||||
import v.scanner
|
||||
import term
|
||||
|
||||
fn test_eval() {
|
||||
|
|
|
@ -71,6 +71,7 @@ pub fn (mut p Preferences) fill_with_defaults() {
|
|||
p.find_cc_if_cross_compiling()
|
||||
p.ccompiler_type = cc_from_string(p.ccompiler)
|
||||
p.is_test = p.path.ends_with('_test.v') || p.path.ends_with('_test.vv')
|
||||
|| p.path.all_before_last('.v').all_before_last('.').ends_with('_test')
|
||||
p.is_vsh = p.path.ends_with('.vsh')
|
||||
p.is_script = p.is_vsh || p.path.ends_with('.v') || p.path.ends_with('.vv')
|
||||
if p.third_party_option == '' {
|
||||
|
|
|
@ -43,9 +43,19 @@ pub enum CompilerType {
|
|||
cplusplus
|
||||
}
|
||||
|
||||
pub enum Arch {
|
||||
_auto
|
||||
amd64 // aka x86_64
|
||||
aarch64 // 64-bit arm
|
||||
aarch32 // 32-bit arm
|
||||
rv64 // 64-bit risc-v
|
||||
rv32 // 32-bit risc-v
|
||||
i386
|
||||
}
|
||||
|
||||
const (
|
||||
list_of_flags_with_param = ['o', 'd', 'define', 'b', 'backend', 'cc', 'os', 'target-os', 'cf',
|
||||
'cflags', 'path']
|
||||
'cflags', 'path', 'arch']
|
||||
)
|
||||
|
||||
[heap]
|
||||
|
@ -54,6 +64,7 @@ pub mut:
|
|||
os OS // the OS to compile for
|
||||
backend Backend
|
||||
build_mode BuildMode
|
||||
arch Arch
|
||||
output_mode OutputMode = .stdout
|
||||
// verbosity VerboseLevel
|
||||
is_verbose bool
|
||||
|
@ -162,6 +173,16 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
|
|||
res.is_apk = true
|
||||
res.build_options << arg
|
||||
}
|
||||
'-arch' {
|
||||
target_arch := cmdline.option(current_args, '-arch', '')
|
||||
i++
|
||||
target_arch_kind := arch_from_string(target_arch) or {
|
||||
eprintln('unknown architercture target `$target_arch`')
|
||||
exit(1)
|
||||
}
|
||||
res.arch = target_arch_kind
|
||||
res.build_options << '$arg $target_arch'
|
||||
}
|
||||
'-show-timings' {
|
||||
res.show_timings = true
|
||||
}
|
||||
|
@ -524,6 +545,41 @@ pub fn (pref &Preferences) vrun_elog(s string) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn arch_from_string(arch_str string) ?Arch {
|
||||
match arch_str {
|
||||
'amd64', 'x86_64', 'x64', 'x86' { // amd64 recommended
|
||||
|
||||
return Arch.amd64
|
||||
}
|
||||
'aarch64', 'arm64' { // aarch64 recommended
|
||||
|
||||
return Arch.aarch64
|
||||
}
|
||||
'arm32', 'aarch32', 'arm' { // aarch32 recommended
|
||||
|
||||
return Arch.aarch32
|
||||
}
|
||||
'rv64', 'riscv64', 'risc-v64', 'riscv', 'risc-v' { // rv64 recommended
|
||||
|
||||
return Arch.rv64
|
||||
}
|
||||
'rv32', 'riscv32' { // rv32 recommended
|
||||
|
||||
return Arch.rv32
|
||||
}
|
||||
'x86_32', 'x32', 'i386', 'IA-32', 'ia-32', 'ia32' { // i386 recommended
|
||||
|
||||
return Arch.i386
|
||||
}
|
||||
'' {
|
||||
return ._auto
|
||||
}
|
||||
else {
|
||||
return error('invalid arch: $arch_str')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn must_exist(path string) {
|
||||
if !os.exists(path) {
|
||||
eprintln('v expects that `$path` exists, but it does not')
|
||||
|
@ -576,6 +632,22 @@ pub fn cc_from_string(cc_str string) CompilerType {
|
|||
return .gcc
|
||||
}
|
||||
|
||||
pub fn get_host_arch() Arch {
|
||||
$if amd64 {
|
||||
return .amd64
|
||||
}
|
||||
// $if i386 {
|
||||
// return .amd64
|
||||
// }
|
||||
$if aarch64 {
|
||||
return .aarch64
|
||||
}
|
||||
// $if aarch32 {
|
||||
// return .aarch32
|
||||
// }
|
||||
panic('unknown host OS')
|
||||
}
|
||||
|
||||
fn parse_define(mut prefs Preferences, define string) {
|
||||
define_parts := define.split('=')
|
||||
prefs.build_options << '-d $define'
|
||||
|
|
|
@ -11,7 +11,8 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
|
|||
if !file.ends_with('.v') && !file.ends_with('.vh') {
|
||||
continue
|
||||
}
|
||||
if file.ends_with('_test.v') {
|
||||
if file.ends_with('_test.v')
|
||||
|| file.all_before_last('.v').all_before_last('.').ends_with('_test') {
|
||||
continue
|
||||
}
|
||||
if prefs.backend == .c && !prefs.should_compile_c(file) {
|
||||
|
@ -20,6 +21,9 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
|
|||
if prefs.backend == .js && !prefs.should_compile_js(file) {
|
||||
continue
|
||||
}
|
||||
if prefs.backend != .js && !prefs.should_compile_asm(file) {
|
||||
continue
|
||||
}
|
||||
if file.contains('_d_') {
|
||||
if prefs.compile_defines_all.len == 0 {
|
||||
continue
|
||||
|
@ -99,7 +103,7 @@ fn fname_without_platform_postfix(file string) string {
|
|||
}
|
||||
|
||||
pub fn (prefs &Preferences) should_compile_c(file string) bool {
|
||||
if !file.ends_with('.c.v') && file.split('.').len > 2 {
|
||||
if file.ends_with('.js.v') {
|
||||
// Probably something like `a.js.v`.
|
||||
return false
|
||||
}
|
||||
|
@ -142,6 +146,24 @@ pub fn (prefs &Preferences) should_compile_c(file string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
pub fn (prefs &Preferences) should_compile_asm(path string) bool {
|
||||
if path.count('.') != 2 || path.ends_with('c.v') || path.ends_with('js.v') {
|
||||
return true
|
||||
}
|
||||
file := path.all_before_last('.v')
|
||||
arch := arch_from_string(file.all_after_last('.')) or { Arch._auto }
|
||||
|
||||
if arch != prefs.arch && prefs.arch != ._auto && arch != ._auto {
|
||||
return false
|
||||
}
|
||||
os := os_from_string(file.all_after_last('_').all_before('.')) or { OS._auto }
|
||||
|
||||
if os != prefs.os && prefs.os != ._auto && os != ._auto {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
pub fn (prefs &Preferences) should_compile_js(file string) bool {
|
||||
if !file.ends_with('.js.v') && file.split('.').len > 2 {
|
||||
// Probably something like `a.c.v`.
|
||||
|
|
|
@ -903,3 +903,38 @@ pub fn (mytable &Table) has_deep_child_no_ref(ts &TypeSymbol, name string) bool
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// bitsize_to_type returns a type corresponding to the bit_size
|
||||
// Examples:
|
||||
//
|
||||
// `8 > i8`
|
||||
//
|
||||
// `32 > int`
|
||||
//
|
||||
// `123 > panic()`
|
||||
//
|
||||
// `128 > [16]byte`
|
||||
//
|
||||
// `608 > [76]byte`
|
||||
pub fn (mut t Table) bitsize_to_type(bit_size int) Type {
|
||||
match bit_size {
|
||||
8 {
|
||||
return i8_type
|
||||
}
|
||||
16 {
|
||||
return i16_type
|
||||
}
|
||||
32 {
|
||||
return int_type
|
||||
}
|
||||
64 {
|
||||
return i64_type
|
||||
}
|
||||
else {
|
||||
if bit_size % 8 != 0 { // there is no way to do `i2131(32)` so this should never be reached
|
||||
panic('compiler bug: bitsizes must be multiples of 8')
|
||||
}
|
||||
return new_type(t.find_or_register_array_fixed(byte_type, bit_size / 8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
module table
|
||||
|
||||
import strings
|
||||
import v.pref
|
||||
|
||||
pub type Type = int
|
||||
|
||||
|
@ -22,6 +23,38 @@ pub enum Language {
|
|||
v
|
||||
c
|
||||
js
|
||||
amd64 // aka x86_64
|
||||
i386
|
||||
aarch64 // 64-bit arm
|
||||
aarch32 // 32-bit arm
|
||||
rv64 // 64-bit risc-v
|
||||
rv32 // 32-bit risc-v
|
||||
}
|
||||
|
||||
pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language {
|
||||
return match pref_arch {
|
||||
.amd64 {
|
||||
Language.amd64
|
||||
}
|
||||
.aarch64 {
|
||||
Language.aarch64
|
||||
}
|
||||
.aarch32 {
|
||||
Language.aarch32
|
||||
}
|
||||
.rv64 {
|
||||
Language.rv64
|
||||
}
|
||||
.rv32 {
|
||||
Language.rv32
|
||||
}
|
||||
.i386 {
|
||||
Language.i386
|
||||
}
|
||||
._auto {
|
||||
Language.v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Represents a type that only needs an identifier, e.g. int, array_int.
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
fn test_inline_asm() {
|
||||
/*
|
||||
// QTODO
|
||||
a := 10
|
||||
b := 0
|
||||
unsafe {
|
||||
asm {
|
||||
"movl %1, %%eax;"
|
||||
"movl %%eax, %0;"
|
||||
:"=r"(b)
|
||||
:"r"(a)
|
||||
:"%eax"
|
||||
}
|
||||
}
|
||||
assert a == 10
|
||||
assert b == 10
|
||||
//
|
||||
e := 0
|
||||
unsafe {
|
||||
asm {
|
||||
//".intel_syntax noprefix;"
|
||||
//"mov %0, 5"
|
||||
"movl $5, %0"
|
||||
:"=a"(e)
|
||||
}
|
||||
}
|
||||
assert e == 5
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import v.tests.assembly.util
|
||||
|
||||
fn test_inline_asm() {
|
||||
a, mut b := 10, 0
|
||||
asm amd64 {
|
||||
mov rax, a
|
||||
mov b, rax
|
||||
; +r (b)
|
||||
; r (a)
|
||||
; rax
|
||||
}
|
||||
assert a == 10
|
||||
assert b == 10
|
||||
|
||||
mut c := 0
|
||||
asm amd64 {
|
||||
mov c, 5
|
||||
; +r (c)
|
||||
}
|
||||
assert c == 5
|
||||
|
||||
d, e, mut f := 10, 2, 0
|
||||
asm amd64 {
|
||||
mov f, d
|
||||
add f, e
|
||||
add f, 5
|
||||
; +r (f) // output
|
||||
; r (d)
|
||||
r (e) // input
|
||||
}
|
||||
assert d == 10
|
||||
assert e == 2
|
||||
assert f == 17
|
||||
|
||||
// g, h, i := 2.3, 4.8, -3.5
|
||||
// asm rv64 {
|
||||
// fadd.s $i, $g, $h // test `.` in instruction name
|
||||
// : =r (i) as i
|
||||
// : r (g) as g
|
||||
// r (g) as h
|
||||
// }
|
||||
// assert g == 2.3
|
||||
// assert h == 4.8
|
||||
// assert i == 7.1
|
||||
|
||||
mut j := 0
|
||||
// do 5*3
|
||||
// adding three, five times
|
||||
asm amd64 {
|
||||
mov rcx, 5 // loop 5 times
|
||||
loop_start:
|
||||
add j, 3
|
||||
loop loop_start
|
||||
; +r (j)
|
||||
; ; rcx
|
||||
}
|
||||
assert j == 5 * 3
|
||||
|
||||
// k := 0 // Wait for tcc to implement goto, and gcc has odd errors
|
||||
// mut loops := 0
|
||||
// outside_label:
|
||||
// if k != 5 {
|
||||
// loops++
|
||||
// asm goto amd64 {
|
||||
// mov k, 1
|
||||
// mov k, 5
|
||||
// jmp outside_label
|
||||
// ; =r (k) as k
|
||||
// ; r (k)
|
||||
// ;
|
||||
// ; outside_label
|
||||
// }
|
||||
// }
|
||||
// assert loops == 1
|
||||
// assert k == 5
|
||||
|
||||
// not marked as mut because we derefernce m to change l
|
||||
l := 5
|
||||
m := &l
|
||||
asm amd64 {
|
||||
movq [m], 7 // have to specify size with q
|
||||
; ; r (m)
|
||||
}
|
||||
assert l == 7
|
||||
|
||||
// same as above
|
||||
n := [5, 9, 0, 4]
|
||||
asm amd64 {
|
||||
loop_start2:
|
||||
addq [in_data + rcx * 4 + 0], 2
|
||||
loop loop_start2
|
||||
addq [in_data + rcx * 4 + 0], 2
|
||||
; ; c (n.len - 1) // c is counter (loop) register
|
||||
r (n.data) as in_data
|
||||
}
|
||||
assert n == [7, 11, 2, 6]
|
||||
|
||||
assert util.add(8, 9, 34, 7) == 58 // test .amd64.v files
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
import v.tests.assembly.util
|
||||
// rename this file to asm_test.amd64.v (and make more for other architectures) once pure v code is enforced
|
||||
|
||||
fn test_inline_asm() {
|
||||
a, mut b := 10, 0
|
||||
asm i386 {
|
||||
mov eax, a
|
||||
mov b, eax
|
||||
; +r (b)
|
||||
; r (a)
|
||||
; eax
|
||||
}
|
||||
assert a == 10
|
||||
assert b == 10
|
||||
|
||||
mut c := 0
|
||||
asm i386 {
|
||||
mov c, 5
|
||||
; +r (c)
|
||||
}
|
||||
assert c == 5
|
||||
|
||||
d, e, mut f := 10, 2, 0
|
||||
asm i386 {
|
||||
mov f, d
|
||||
add f, e
|
||||
add f, 5
|
||||
; +r (f) // output
|
||||
; r (d)
|
||||
r (e) // input
|
||||
}
|
||||
assert d == 10
|
||||
assert e == 2
|
||||
assert f == 17
|
||||
|
||||
// g, h, i := 2.3, 4.8, -3.5
|
||||
// asm rv64 {
|
||||
// fadd.s $i, $g, $h // test `.` in instruction name
|
||||
// : =r (i) as i
|
||||
// : r (g) as g
|
||||
// r (g) as h
|
||||
// }
|
||||
// assert g == 2.3
|
||||
// assert h == 4.8
|
||||
// assert i == 7.1
|
||||
|
||||
mut j := 0
|
||||
// do 5*3
|
||||
// adding three, five times
|
||||
asm i386 {
|
||||
mov ecx, 5 // loop 5 times
|
||||
loop_start:
|
||||
add j, 3
|
||||
loop loop_start
|
||||
; +r (j)
|
||||
; ; ecx
|
||||
}
|
||||
assert j == 5 * 3
|
||||
|
||||
// k := 0 // Wait for tcc to implement goto, and gcc has odd errors
|
||||
// mut loops := 0
|
||||
// outside_label:
|
||||
// if k != 5 {
|
||||
// loops++
|
||||
// asm goto amd64 {
|
||||
// mov k, 1
|
||||
// mov k, 5
|
||||
// jmp outside_label
|
||||
// ; =r (k) as k
|
||||
// ; r (k)
|
||||
// ;
|
||||
// ; outside_label
|
||||
// }
|
||||
// }
|
||||
// assert loops == 1
|
||||
// assert k == 5
|
||||
|
||||
// not marked as mut because we derefernce m to change l
|
||||
l := 5
|
||||
m := &l
|
||||
asm i386 {
|
||||
movd [m], 7 // have to specify size with q
|
||||
; ; r (m)
|
||||
}
|
||||
assert l == 7
|
||||
|
||||
// same as above
|
||||
n := [5, 9, 0, 4]
|
||||
asm i386 {
|
||||
loop_start2:
|
||||
addd [in_data + ecx * 4 + 0], 2
|
||||
loop loop_start2
|
||||
addd [in_data + ecx * 4 + 0], 2
|
||||
; ; c (n.len - 1) // c is counter (loop) register
|
||||
r (n.data) as in_data
|
||||
}
|
||||
assert n == [7, 11, 2, 6]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
module util
|
||||
|
||||
pub fn add(a ...int) int {
|
||||
mut res := 0
|
||||
asm amd64 {
|
||||
loop_start3:
|
||||
addq rax, [in_data + rcx * 4 + 0]
|
||||
loop loop_start3
|
||||
addq rax, [in_data + rcx * 4 + 0]
|
||||
; +a (res)
|
||||
; c (a.len - 1) // c is counter (loop) register
|
||||
r (a.data) as in_data
|
||||
}
|
||||
return res
|
||||
}
|
Loading…
Reference in New Issue