all: reimplement inline assembly (#8645)
parent
dd9f9c2718
commit
fafb035fb5
|
@ -196,11 +196,6 @@ pub fn (mut ts TestSession) test() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$if tinyc {
|
|
||||||
if file.contains('asm') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
remaining_files << dot_relative_file
|
remaining_files << dot_relative_file
|
||||||
}
|
}
|
||||||
remaining_files = vtest.filter_vtest_only(remaining_files, fix_slashes: false)
|
remaining_files = vtest.filter_vtest_only(remaining_files, fix_slashes: false)
|
||||||
|
|
|
@ -3,6 +3,7 @@ module main
|
||||||
import os
|
import os
|
||||||
import os.cmdline
|
import os.cmdline
|
||||||
import testing
|
import testing
|
||||||
|
import v.pref
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
args := os.args.clone()
|
args := os.args.clone()
|
||||||
|
@ -18,20 +19,35 @@ fn main() {
|
||||||
eprintln('Use `v test-all` instead.')
|
eprintln('Use `v test-all` instead.')
|
||||||
exit(1)
|
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(' '))
|
mut ts := testing.new_test_session(args_before.join(' '))
|
||||||
for targ in args_after {
|
for targ in args_after {
|
||||||
if os.exists(targ) && targ.ends_with('_test.v') {
|
|
||||||
ts.files << targ
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if os.is_dir(targ) {
|
if os.is_dir(targ) {
|
||||||
// Fetch all tests from the directory
|
// 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
|
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...')
|
testing.header('Testing...')
|
||||||
ts.test()
|
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(' NB: you can also give many and mixed folder/ file_test.v arguments after `v test` .')
|
||||||
println('')
|
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.
|
are auto generated when the operators are defined though they must return the same type.
|
||||||
|
|
||||||
## Inline assembly
|
## Inline assembly
|
||||||
|
<!-- ignore because it doesn't pass fmt test (why?) -->
|
||||||
TODO: not implemented yet
|
```v ignore
|
||||||
|
a := 100
|
||||||
```v failcompile
|
b := 20
|
||||||
fn main() {
|
mut c := 0
|
||||||
a := 10
|
asm amd64 {
|
||||||
asm x64 {
|
mov eax, a
|
||||||
mov eax, [a]
|
add eax, b
|
||||||
add eax, 10
|
mov c, eax
|
||||||
mov [a], 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
|
## Translating C to V
|
||||||
|
|
||||||
TODO: translating C to V will be available in V 0.3.
|
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
|
// This program displays the fibonacci sequence
|
||||||
import os
|
// import os
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Check for user input
|
// Check for user input
|
||||||
if os.args.len != 2 {
|
//if os.args.len != 2 {
|
||||||
println('usage: fibonacci [rank]')
|
// println('usage: fibonacci [rank]')
|
||||||
|
|
||||||
// Exit
|
// Exit
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Parse first argument and cast it to int
|
// 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
|
// Can only calculate correctly until rank 92
|
||||||
if stop > 92 {
|
if stop > 92 {
|
||||||
println('rank must be 92 or less')
|
println('rank must be 92 or less')
|
||||||
|
@ -20,10 +20,10 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Three consecutive terms of the sequence
|
// Three consecutive terms of the sequence
|
||||||
mut a := u64(0)
|
mut a := 0
|
||||||
mut b := u64(0)
|
mut b := 0
|
||||||
mut c := u64(1)
|
mut c := 1
|
||||||
|
println(a+c+c)
|
||||||
for _ in 0 .. stop {
|
for _ in 0 .. stop {
|
||||||
// Set a and b to the next term
|
// Set a and b to the next term
|
||||||
a = b
|
a = b
|
||||||
|
|
|
@ -4,17 +4,17 @@ pub enum Linux_mem {
|
||||||
page_size = 4096
|
page_size = 4096
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Wp_sys {
|
pub const (
|
||||||
wnohang = 0x00000001
|
wp_sys_wnohang = u64(0x00000001)
|
||||||
wuntraced = 0x00000002
|
wp_sys_wuntraced = u64(0x00000002)
|
||||||
wstopped = 0x00000002
|
wp_sys_wstopped = u64(0x00000002)
|
||||||
wexited = 0x00000004
|
wp_sys_wexited = u64(0x00000004)
|
||||||
wcontinued = 0x00000008
|
wp_sys_wcontinued = u64(0x00000008)
|
||||||
wnowait = 0x01000000 // don't reap, just poll status.
|
wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status.
|
||||||
__wnothread = 0x20000000 // don't wait on children of other threads in this group
|
wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group
|
||||||
__wall = 0x40000000 // wait on all children, regardless of type
|
wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type
|
||||||
__wclone = 0x80000000 // wait only on non-sigchld children
|
wp_sys___wclone = u64(0x80000000) // wait only on non-sigchld children
|
||||||
}
|
)
|
||||||
|
|
||||||
// First argument to waitid:
|
// First argument to waitid:
|
||||||
pub enum Wi_which {
|
pub enum Wi_which {
|
||||||
|
@ -32,7 +32,8 @@ pub enum Wi_si_code {
|
||||||
cld_continued = 6 // stopped child has continued
|
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
|
Upon successful return, waitid() fills in the
|
||||||
following fields of the siginfo_t structure
|
following fields of the siginfo_t structure
|
||||||
|
@ -74,98 +75,96 @@ pub enum Sig_index {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Signo {
|
pub enum Signo {
|
||||||
sighup = 1 // Hangup.
|
sighup = 1 // Hangup.
|
||||||
sigint = 2 // Interactive attention signal.
|
sigint = 2 // Interactive attention signal.
|
||||||
sigquit = 3 // Quit.
|
sigquit = 3 // Quit.
|
||||||
sigill = 4 // Illegal instruction.
|
sigill = 4 // Illegal instruction.
|
||||||
sigtrap = 5 // Trace/breakpoint trap.
|
sigtrap = 5 // Trace/breakpoint trap.
|
||||||
sigabrt = 6 // Abnormal termination.
|
sigabrt = 6 // Abnormal termination.
|
||||||
sigbus = 7
|
sigbus = 7
|
||||||
sigfpe = 8 // Erroneous arithmetic operation.
|
sigfpe = 8 // Erroneous arithmetic operation.
|
||||||
sigkill = 9 // Killed.
|
sigkill = 9 // Killed.
|
||||||
sigusr1 = 10
|
sigusr1 = 10
|
||||||
sigsegv = 11 // Invalid access to storage.
|
sigsegv = 11 // Invalid access to storage.
|
||||||
sigusr2 = 12
|
sigusr2 = 12
|
||||||
sigpipe = 13 // Broken pipe.
|
sigpipe = 13 // Broken pipe.
|
||||||
sigalrm = 14 // Alarm clock.
|
sigalrm = 14 // Alarm clock.
|
||||||
sigterm = 15 // Termination request.
|
sigterm = 15 // Termination request.
|
||||||
sigstkflt = 16
|
sigstkflt = 16
|
||||||
sigchld = 17
|
sigchld = 17
|
||||||
sigcont = 18
|
sigcont = 18
|
||||||
sigstop = 19
|
sigstop = 19
|
||||||
sigtstp = 20
|
sigtstp = 20
|
||||||
sigttin = 21 // Background read from control terminal.
|
sigttin = 21 // Background read from control terminal.
|
||||||
sigttou = 22 // Background write to control terminal.
|
sigttou = 22 // Background write to control terminal.
|
||||||
sigurg = 23
|
sigurg = 23
|
||||||
sigxcpu = 24 // CPU time limit exceeded.
|
sigxcpu = 24 // CPU time limit exceeded.
|
||||||
sigxfsz = 25 // File size limit exceeded.
|
sigxfsz = 25 // File size limit exceeded.
|
||||||
sigvtalrm = 26 // Virtual timer expired.
|
sigvtalrm = 26 // Virtual timer expired.
|
||||||
sigprof = 27 // Profiling timer expired.
|
sigprof = 27 // Profiling timer expired.
|
||||||
sigwinch = 28
|
sigwinch = 28
|
||||||
sigpoll = 29
|
sigpoll = 29
|
||||||
sigsys = 31
|
sigsys = 31
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const (
|
||||||
pub enum Fcntl {
|
fcntlf_dupfd = 0x00000000
|
||||||
fd_cloexec = 0x00000001
|
fcntlf_exlck = 0x00000004
|
||||||
f_dupfd = 0x00000000
|
fcntlf_getfd = 0x00000001
|
||||||
f_exlck = 0x00000004
|
fcntlf_getfl = 0x00000003
|
||||||
f_getfd = 0x00000001
|
fcntlf_getlk = 0x00000005
|
||||||
f_getfl = 0x00000003
|
fcntlf_getlk64 = 0x0000000c
|
||||||
f_getlk = 0x00000005
|
fcntlf_getown = 0x00000009
|
||||||
f_getlk64 = 0x0000000c
|
fcntlf_getowner_uids = 0x00000011
|
||||||
f_getown = 0x00000009
|
fcntlf_getown_ex = 0x00000010
|
||||||
f_getowner_uids = 0x00000011
|
fcntlf_getsig = 0x0000000b
|
||||||
f_getown_ex = 0x00000010
|
fcntlf_ofd_getlk = 0x00000024
|
||||||
f_getsig = 0x0000000b
|
fcntlf_ofd_setlk = 0x00000025
|
||||||
f_ofd_getlk = 0x00000024
|
fcntlf_ofd_setlkw = 0x00000026
|
||||||
f_ofd_setlk = 0x00000025
|
fcntlf_owner_pgrp = 0x00000002
|
||||||
f_ofd_setlkw = 0x00000026
|
fcntlf_owner_pid = 0x00000001
|
||||||
f_owner_pgrp = 0x00000002
|
fcntlf_owner_tid = 0x00000000
|
||||||
f_owner_pid = 0x00000001
|
fcntlf_rdlck = 0x00000000
|
||||||
f_owner_tid = 0x00000000
|
fcntlf_setfd = 0x00000002
|
||||||
f_rdlck = 0x00000000
|
fcntlf_setfl = 0x00000004
|
||||||
f_setfd = 0x00000002
|
fcntlf_setlk = 0x00000006
|
||||||
f_setfl = 0x00000004
|
fcntlf_setlk64 = 0x0000000d
|
||||||
f_setlk = 0x00000006
|
fcntlf_setlkw = 0x00000007
|
||||||
f_setlk64 = 0x0000000d
|
fcntlf_setlkw64 = 0x0000000e
|
||||||
f_setlkw = 0x00000007
|
fcntlf_setown = 0x00000008
|
||||||
f_setlkw64 = 0x0000000e
|
fcntlf_setown_ex = 0x0000000f
|
||||||
f_setown = 0x00000008
|
fcntlf_setsig = 0x0000000a
|
||||||
f_setown_ex = 0x0000000f
|
fcntlf_shlck = 0x00000008
|
||||||
f_setsig = 0x0000000a
|
fcntlf_unlck = 0x00000002
|
||||||
f_shlck = 0x00000008
|
fcntlf_wrlck = 0x00000001
|
||||||
f_unlck = 0x00000002
|
fcntllock_ex = 0x00000002
|
||||||
f_wrlck = 0x00000001
|
fcntllock_mand = 0x00000020
|
||||||
lock_ex = 0x00000002
|
fcntllock_nb = 0x00000004
|
||||||
lock_mand = 0x00000020
|
fcntllock_read = 0x00000040
|
||||||
lock_nb = 0x00000004
|
fcntllock_rw = 0x000000c0
|
||||||
lock_read = 0x00000040
|
fcntllock_sh = 0x00000001
|
||||||
lock_rw = 0x000000c0
|
fcntllock_un = 0x00000008
|
||||||
lock_sh = 0x00000001
|
fcntllock_write = 0x00000080
|
||||||
lock_un = 0x00000008
|
fcntlo_accmode = 0x00000003
|
||||||
lock_write = 0x00000080
|
fcntlo_append = 0x00000400
|
||||||
o_accmode = 0x00000003
|
fcntlo_cloexec = 0x00080000
|
||||||
o_append = 0x00000400
|
fcntlo_creat = 0x00000040
|
||||||
o_cloexec = 0x00080000
|
fcntlo_direct = 0x00004000
|
||||||
o_creat = 0x00000040
|
fcntlo_directory = 0x00010000
|
||||||
o_direct = 0x00004000
|
fcntlo_dsync = 0x00001000
|
||||||
o_directory = 0x00010000
|
fcntlo_excl = 0x00000080
|
||||||
o_dsync = 0x00001000
|
fcntlo_largefile = 0x00008000
|
||||||
o_excl = 0x00000080
|
fcntlo_ndelay = 0x00000800
|
||||||
o_largefile = 0x00008000
|
fcntlo_noatime = 0x00040000
|
||||||
o_ndelay = 0x00000800
|
fcntlo_noctty = 0x00000100
|
||||||
o_noatime = 0x00040000
|
fcntlo_nofollow = 0x00020000
|
||||||
o_noctty = 0x00000100
|
fcntlo_nonblock = 0x00000800
|
||||||
o_nofollow = 0x00020000
|
fcntlo_path = 0x00200000
|
||||||
o_nonblock = 0x00000800
|
fcntlo_rdonly = 0x00000000
|
||||||
o_path = 0x00200000
|
fcntlo_rdwr = 0x00000002
|
||||||
o_rdonly = 0x00000000
|
fcntlo_trunc = 0x00000200
|
||||||
o_rdwr = 0x00000002
|
fcntlo_wronly = 0x00000001
|
||||||
o_trunc = 0x00000200
|
)
|
||||||
o_wronly = 0x00000001
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Errno {
|
pub enum Errno {
|
||||||
enoerror = 0x00000000
|
enoerror = 0x00000000
|
||||||
|
@ -222,104 +221,106 @@ pub enum Map_flags {
|
||||||
map_fixed = 0x10
|
map_fixed = 0x10
|
||||||
map_file = 0x00
|
map_file = 0x00
|
||||||
map_anonymous = 0x20
|
map_anonymous = 0x20
|
||||||
map_anon = 0x20
|
|
||||||
map_huge_shift = 26
|
map_huge_shift = 26
|
||||||
map_huge_mask = 0x3f
|
map_huge_mask = 0x3f
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_not_call_me_asm_keeper0() {
|
fn sys_call0(scn u64) u64 {
|
||||||
unsafe {
|
res := u64(0)
|
||||||
asm {
|
asm amd64 {
|
||||||
"\n"
|
syscall
|
||||||
"ret\n"
|
; =a (res)
|
||||||
""
|
; a (scn)
|
||||||
".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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sys_call0(scn u64) u64
|
fn sys_call1(scn u64, arg1 u64) u64 {
|
||||||
fn sys_call1(scn, arg1 u64) u64
|
res := u64(0)
|
||||||
fn sys_call2(scn, arg1, arg2 u64) u64
|
asm amd64 {
|
||||||
fn sys_call3(scn, arg1, arg2, arg3 u64) u64
|
syscall
|
||||||
fn sys_call4(scn, arg1, arg2, arg3, arg4 u64) u64
|
; =a (res)
|
||||||
fn sys_call5(scn, arg1, arg2, arg3, arg4, arg5 u64) u64
|
; a (scn)
|
||||||
fn sys_call6(scn, arg1, arg2, arg3, arg4, arg5, arg6 u64) u64
|
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) {
|
fn split_int_errno(rc_in u64) (i64, Errno) {
|
||||||
rc := i64(rc_in)
|
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
|
// 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))
|
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))
|
return split_int_errno(sys_call3(1, u64(fd), u64(buf), count))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sys_open(filename byteptr, flags Fcntl, mode int) (i64, Errno) {
|
pub fn sys_open(filename byteptr, flags i64, mode int) (i64, Errno) {
|
||||||
//2 sys_open const char *filename int flags int mode
|
// 2 sys_open const char *filename int flags int mode
|
||||||
return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(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
|
// 33 sys_dup2 unsigned int oldfd unsigned int newfd
|
||||||
pub fn sys_dup2 (oldfd, newfd int) (i64, Errno) {
|
pub fn sys_dup2(oldfd int, newfd int) (i64, Errno) {
|
||||||
return split_int_errno(sys_call2(33, u64(oldfd),u64(newfd)))
|
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[]
|
||||||
//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 {
|
||||||
//pub fn sys_execve(filename byteptr, argv []byteptr, envp []byteptr) int {
|
|
||||||
// return sys_call3(59, filename, argv, envp)
|
// return sys_call3(59, filename, argv, envp)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
// 60 sys_exit int error_code
|
// 60 sys_exit int error_code
|
||||||
pub fn sys_exit (ec int) {
|
pub fn sys_exit(ec int) {
|
||||||
sys_call1(60, u64(ec))
|
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
|
// 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)))
|
return Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A few years old, but still relevant
|
A few years old, but still relevant
|
||||||
https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
|
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.token
|
||||||
import v.table
|
import v.table
|
||||||
import v.errors
|
import v.errors
|
||||||
|
import v.pref
|
||||||
|
|
||||||
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
|
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 |
|
RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
|
||||||
StructInit | Type | TypeOf | UnsafeExpr
|
StructInit | Type | TypeOf | UnsafeExpr
|
||||||
|
|
||||||
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
|
pub type Stmt = AsmStmt | AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl |
|
||||||
EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
|
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
|
||||||
GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return | SqlStmt |
|
GoStmt | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | Return |
|
||||||
StructDecl | TypeDecl
|
SqlStmt | StructDecl | TypeDecl
|
||||||
|
|
||||||
// NB: when you add a new Expr or Stmt type with a .pos field, remember to update
|
// NB: when you add a new Expr or Stmt type with a .pos field, remember to update
|
||||||
// the .position() token.Position methods too.
|
// the .position() token.Position methods too.
|
||||||
pub type ScopeObject = ConstField | GlobalField | Var
|
pub type ScopeObject = AsmRegister | ConstField | GlobalField | Var
|
||||||
|
|
||||||
// TOOD: replace table.Param
|
// TOOD: replace table.Param
|
||||||
pub type Node = ConstField | EnumField | Expr | Field | File | GlobalField | IfBranch |
|
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
|
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 struct AssertStmt {
|
||||||
pub:
|
pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
@ -1383,7 +1553,17 @@ pub fn (node Node) position() token.Position {
|
||||||
}
|
}
|
||||||
ScopeObject {
|
ScopeObject {
|
||||||
match node {
|
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 {
|
File {
|
||||||
|
@ -1510,6 +1690,7 @@ pub fn (node Node) children() []Node {
|
||||||
} else if node is ScopeObject {
|
} else if node is ScopeObject {
|
||||||
match node {
|
match node {
|
||||||
GlobalField, ConstField, Var { children << node.expr }
|
GlobalField, ConstField, Var { children << node.expr }
|
||||||
|
AsmRegister {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match node {
|
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
|
// builtin C.m*, C.s* only - temp
|
||||||
c.warn('function `$f.name` must be called from an `unsafe` block', call_expr.pos)
|
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)
|
c.error('cannot call a function that does not have a body', call_expr.pos)
|
||||||
}
|
}
|
||||||
for generic_type in call_expr.generic_types {
|
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
|
// c.expected_type = table.void_type
|
||||||
match mut node {
|
match mut node {
|
||||||
|
ast.AsmStmt {
|
||||||
|
c.asm_stmt(mut node)
|
||||||
|
}
|
||||||
ast.AssertStmt {
|
ast.AssertStmt {
|
||||||
c.assert_stmt(node)
|
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) {
|
fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
|
||||||
if c.skip_flags {
|
if c.skip_flags {
|
||||||
return
|
return
|
||||||
|
@ -3998,6 +4108,23 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
||||||
return table.void_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 {
|
pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) table.Type {
|
||||||
node.expr_type = c.expr(node.expr) // type to be casted
|
node.expr_type = c.expr(node.expr) // type to be casted
|
||||||
from_type_sym := c.table.get_type_symbol(node.expr_type)
|
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
|
// TODO: remove once we have better type inference
|
||||||
mut name := ''
|
mut name := ''
|
||||||
match obj {
|
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{}
|
mut expr := ast.Expr{}
|
||||||
if obj is ast.Var {
|
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,6 +178,8 @@ fn (mut tasks Tasks) run() {
|
||||||
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'
|
||||||
}
|
}
|
||||||
$if msvc {
|
$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
|
// 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_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'
|
||||||
|
|
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}')
|
eprintln('stmt: ${node.pos:-42} | node: ${node.type_name():-20}')
|
||||||
}
|
}
|
||||||
match node {
|
match node {
|
||||||
|
ast.AsmStmt {
|
||||||
|
f.asm_stmt(node)
|
||||||
|
}
|
||||||
ast.AssignStmt {
|
ast.AssignStmt {
|
||||||
f.assign_stmt(node)
|
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 {
|
fn stmt_is_single_line(stmt ast.Stmt) bool {
|
||||||
return match stmt {
|
return match stmt {
|
||||||
ast.ExprStmt, ast.AssertStmt { expr_is_single_line(stmt.expr) }
|
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')
|
f.write('none')
|
||||||
}
|
}
|
||||||
ast.OrExpr {
|
ast.OrExpr {
|
||||||
// shouldn't happen, an or expression is always linked to a call expr
|
// shouldn't happen, an or expression is always linked to a call expr or index expr
|
||||||
panic('fmt: OrExpr should be linked to CallExpr')
|
panic('fmt: OrExpr should be linked to ast.CallExpr or ast.IndexExpr')
|
||||||
}
|
}
|
||||||
ast.ParExpr {
|
ast.ParExpr {
|
||||||
f.par_expr(node)
|
f.par_expr(node)
|
||||||
|
|
|
@ -19,7 +19,9 @@ const (
|
||||||
'delete', 'do', 'double', 'else', 'enum', 'error', 'exit', 'export', 'extern', 'float',
|
'delete', 'do', 'double', 'else', 'enum', 'error', 'exit', 'export', 'extern', 'float',
|
||||||
'for', 'free', 'goto', 'if', 'inline', 'int', 'link', 'long', 'malloc', 'namespace', 'new',
|
'for', 'free', 'goto', 'if', 'inline', 'int', 'link', 'long', 'malloc', 'namespace', 'new',
|
||||||
'panic', 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct',
|
'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
|
// same order as in token.Kind
|
||||||
cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le']
|
cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le']
|
||||||
// when operands are switched
|
// when operands are switched
|
||||||
|
@ -1001,6 +1003,10 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
// println('g.stmt()')
|
// println('g.stmt()')
|
||||||
// g.writeln('//// stmt start')
|
// g.writeln('//// stmt start')
|
||||||
match node {
|
match node {
|
||||||
|
ast.AsmStmt {
|
||||||
|
g.write_v_source_line_info(node.pos)
|
||||||
|
g.gen_asm_stmt(node)
|
||||||
|
}
|
||||||
ast.AssertStmt {
|
ast.AssertStmt {
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
g.gen_assert_stmt(node)
|
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 {
|
fn cnewlines(s string) string {
|
||||||
return s.replace('\n', r'\n')
|
return s.replace('\n', r'\n')
|
||||||
}
|
}
|
||||||
|
@ -3116,6 +3293,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
left_type := g.unwrap_generic(node.left_type)
|
left_type := g.unwrap_generic(node.left_type)
|
||||||
|
// println('>>$node')
|
||||||
left_sym := g.table.get_type_symbol(left_type)
|
left_sym := g.table.get_type_symbol(left_type)
|
||||||
left_final_sym := g.table.get_final_type_symbol(left_type)
|
left_final_sym := g.table.get_final_type_symbol(left_type)
|
||||||
unaliased_left := if left_sym.kind == .alias {
|
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) {
|
fn (mut g JsGen) stmt(node ast.Stmt) {
|
||||||
g.stmt_start_pos = g.ns.out.len
|
g.stmt_start_pos = g.ns.out.len
|
||||||
match node {
|
match node {
|
||||||
|
ast.AsmStmt {
|
||||||
|
panic('inline asm is not supported by js')
|
||||||
|
}
|
||||||
ast.AssertStmt {
|
ast.AssertStmt {
|
||||||
g.gen_assert_stmt(node)
|
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) {
|
pub fn (mut w Walker) stmt(node ast.Stmt) {
|
||||||
match mut node {
|
match mut node {
|
||||||
|
ast.AsmStmt {
|
||||||
|
w.asm_io(node.output)
|
||||||
|
w.asm_io(node.input)
|
||||||
|
}
|
||||||
ast.AssertStmt {
|
ast.AssertStmt {
|
||||||
w.expr(node.expr)
|
w.expr(node.expr)
|
||||||
w.n_asserts++
|
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) {
|
fn (mut w Walker) defer_stmts(stmts []ast.DeferStmt) {
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
w.stmts(stmt.stmts)
|
w.stmts(stmt.stmts)
|
||||||
|
@ -145,15 +155,15 @@ fn (mut w Walker) expr(node ast.Expr) {
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
w.fn_decl(mut node.decl)
|
w.fn_decl(mut node.decl)
|
||||||
}
|
}
|
||||||
ast.Assoc {
|
|
||||||
w.exprs(node.exprs)
|
|
||||||
}
|
|
||||||
ast.ArrayInit {
|
ast.ArrayInit {
|
||||||
w.expr(node.len_expr)
|
w.expr(node.len_expr)
|
||||||
w.expr(node.cap_expr)
|
w.expr(node.cap_expr)
|
||||||
w.expr(node.default_expr)
|
w.expr(node.default_expr)
|
||||||
w.exprs(node.exprs)
|
w.exprs(node.exprs)
|
||||||
}
|
}
|
||||||
|
ast.Assoc {
|
||||||
|
w.exprs(node.exprs)
|
||||||
|
}
|
||||||
ast.ArrayDecompose {
|
ast.ArrayDecompose {
|
||||||
w.expr(node.expr)
|
w.expr(node.expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,52 +23,55 @@ mut:
|
||||||
file_base string // "hello.v"
|
file_base string // "hello.v"
|
||||||
file_name string // "/home/user/hello.v"
|
file_name string // "/home/user/hello.v"
|
||||||
file_name_dir string // "/home/user"
|
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
|
scanner &scanner.Scanner
|
||||||
comments_mode scanner.CommentsMode = .skip_comments
|
comments_mode scanner.CommentsMode = .skip_comments
|
||||||
// see comment in parse_file
|
// see comment in parse_file
|
||||||
tok token.Token
|
tok token.Token
|
||||||
prev_tok token.Token
|
prev_tok token.Token
|
||||||
peek_tok token.Token
|
peek_tok token.Token
|
||||||
table &table.Table
|
table &table.Table
|
||||||
language table.Language
|
language table.Language
|
||||||
inside_if bool
|
inside_if bool
|
||||||
inside_if_expr bool
|
inside_if_expr bool
|
||||||
inside_ct_if_expr bool
|
inside_ct_if_expr bool
|
||||||
inside_or_expr bool
|
inside_or_expr bool
|
||||||
inside_for bool
|
inside_for bool
|
||||||
inside_fn bool // true even with implicit main
|
inside_fn bool // true even with implicit main
|
||||||
inside_unsafe_fn bool // true when in fn, marked with `[unsafe]`
|
inside_unsafe_fn bool
|
||||||
inside_str_interp bool
|
inside_str_interp bool
|
||||||
or_is_handled bool // ignore `or` in this expression
|
or_is_handled bool // ignore `or` in this expression
|
||||||
builtin_mod bool // are we in the `builtin` module?
|
builtin_mod bool // are we in the `builtin` module?
|
||||||
mod string // current module name
|
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
|
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
|
attrs []table.Attr // attributes before next decl stmt
|
||||||
expr_mod string // for constructing full type names in parse_type()
|
expr_mod string // for constructing full type names in parse_type()
|
||||||
scope &ast.Scope
|
scope &ast.Scope
|
||||||
global_scope &ast.Scope
|
global_scope &ast.Scope
|
||||||
imports map[string]string // alias => mod_name
|
imports map[string]string // alias => mod_name
|
||||||
ast_imports []ast.Import // mod_names
|
ast_imports []ast.Import // mod_names
|
||||||
used_imports []string // alias
|
used_imports []string // alias
|
||||||
auto_imports []string // imports, the user does not need to specify
|
auto_imports []string // imports, the user does not need to specify
|
||||||
imported_symbols map[string]string
|
imported_symbols map[string]string
|
||||||
is_amp bool // for generating the right code for `&Foo{}`
|
is_amp bool // for generating the right code for `&Foo{}`
|
||||||
returns bool
|
returns bool
|
||||||
inside_match bool // to separate `match A { }` from `Struct{}`
|
inside_match bool // to separate `match A { }` from `Struct{}`
|
||||||
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
||||||
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
||||||
inside_match_body bool // to fix eval not used TODO
|
inside_match_body bool // to fix eval not used TODO
|
||||||
inside_unsafe bool
|
inside_unsafe bool
|
||||||
is_stmt_ident bool // true while the beginning of a statement is an ident/selector
|
is_stmt_ident bool // true while the beginning of a statement is an ident/selector
|
||||||
expecting_type bool // `is Type`, expecting type
|
expecting_type bool // `is Type`, expecting type
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
warnings []errors.Warning
|
warnings []errors.Warning
|
||||||
vet_errors []vet.Error
|
vet_errors []vet.Error
|
||||||
cur_fn_name string
|
cur_fn_name string
|
||||||
label_names []string
|
label_names []string
|
||||||
in_generic_params bool // indicates if parsing between `<` and `>` of a method/function
|
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
|
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
|
// for tests
|
||||||
|
@ -134,14 +137,28 @@ pub fn (mut p Parser) set_path(path string) {
|
||||||
p.file_name = path
|
p.file_name = path
|
||||||
p.file_base = os.base(path)
|
p.file_base = os.base(path)
|
||||||
p.file_name_dir = os.dir(path)
|
p.file_name_dir = os.dir(path)
|
||||||
if path.ends_with('_c.v') || path.ends_with('.c.v') || path.ends_with('.c.vv')
|
before_dot_v := path.before('.v') // also works for .vv and .vsh
|
||||||
|| path.ends_with('.c.vsh') {
|
language := before_dot_v.all_after_last('.')
|
||||||
p.file_backend_mode = .c
|
langauge_with_underscore := before_dot_v.all_after_last('_')
|
||||||
} else if path.ends_with('_js.v') || path.ends_with('.js.v') || path.ends_with('.js.vv')
|
if language == before_dot_v && langauge_with_underscore == before_dot_v {
|
||||||
|| path.ends_with('.js.vsh') {
|
|
||||||
p.file_backend_mode = .js
|
|
||||||
} else {
|
|
||||||
p.file_backend_mode = .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() {
|
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()
|
||||||
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] {
|
// for p.tok.kind in [.line_comment, .mline_comment] {
|
||||||
// p.next()
|
// p.next()
|
||||||
// }
|
// }
|
||||||
if p.tok.kind == expected {
|
|
||||||
|
if _likely_(p.tok.kind == expected) {
|
||||||
p.next()
|
p.next()
|
||||||
} else {
|
} else {
|
||||||
if expected == .name {
|
if expected == .name {
|
||||||
|
@ -507,6 +525,9 @@ pub fn (mut p Parser) top_stmt() ast.Stmt {
|
||||||
p.attributes()
|
p.attributes()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
.key_asm {
|
||||||
|
return p.asm_stmt(true)
|
||||||
|
}
|
||||||
.key_interface {
|
.key_interface {
|
||||||
return p.interface_decl()
|
return p.interface_decl()
|
||||||
}
|
}
|
||||||
|
@ -802,6 +823,9 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
||||||
p.tok.position())
|
p.tok.position())
|
||||||
return ast.Stmt{}
|
return ast.Stmt{}
|
||||||
}
|
}
|
||||||
|
.key_asm {
|
||||||
|
return p.asm_stmt(false)
|
||||||
|
}
|
||||||
// literals, 'if', etc. in here
|
// literals, 'if', etc. in here
|
||||||
else {
|
else {
|
||||||
return p.parse_multi_expr(is_top_level)
|
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) {
|
fn (mut p Parser) expr_list() ([]ast.Expr, []ast.Comment) {
|
||||||
mut exprs := []ast.Expr{}
|
mut exprs := []ast.Expr{}
|
||||||
mut comments := []ast.Comment{}
|
mut comments := []ast.Comment{}
|
||||||
|
|
|
@ -322,7 +322,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
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
|
// eof should be handled where it happens
|
||||||
p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.position())
|
p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.position())
|
||||||
return ast.Expr{}
|
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 {
|
} else if p.tok.kind == .key_as {
|
||||||
// sum type as cast `x := SumType as Variant`
|
// sum type as cast `x := SumType as Variant`
|
||||||
pos := p.tok.position()
|
if !p.inside_asm {
|
||||||
p.next()
|
pos := p.tok.position()
|
||||||
typ := p.parse_type()
|
p.next()
|
||||||
node = ast.AsCast{
|
typ := p.parse_type()
|
||||||
expr: node
|
node = ast.AsCast{
|
||||||
typ: typ
|
expr: node
|
||||||
pos: pos
|
typ: typ
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return node
|
||||||
}
|
}
|
||||||
} else if p.tok.kind == .left_shift && p.is_stmt_ident {
|
} else if p.tok.kind == .left_shift && p.is_stmt_ident {
|
||||||
// arr << elem
|
// arr << elem
|
||||||
|
|
|
@ -6,6 +6,7 @@ import v.gen.c
|
||||||
import v.table
|
import v.table
|
||||||
import v.checker
|
import v.checker
|
||||||
import v.pref
|
import v.pref
|
||||||
|
import v.scanner
|
||||||
import term
|
import term
|
||||||
|
|
||||||
fn test_eval() {
|
fn test_eval() {
|
||||||
|
|
|
@ -71,6 +71,7 @@ pub fn (mut p Preferences) fill_with_defaults() {
|
||||||
p.find_cc_if_cross_compiling()
|
p.find_cc_if_cross_compiling()
|
||||||
p.ccompiler_type = cc_from_string(p.ccompiler)
|
p.ccompiler_type = cc_from_string(p.ccompiler)
|
||||||
p.is_test = p.path.ends_with('_test.v') || p.path.ends_with('_test.vv')
|
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_vsh = p.path.ends_with('.vsh')
|
||||||
p.is_script = p.is_vsh || p.path.ends_with('.v') || p.path.ends_with('.vv')
|
p.is_script = p.is_vsh || p.path.ends_with('.v') || p.path.ends_with('.vv')
|
||||||
if p.third_party_option == '' {
|
if p.third_party_option == '' {
|
||||||
|
|
|
@ -43,9 +43,19 @@ pub enum CompilerType {
|
||||||
cplusplus
|
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 (
|
const (
|
||||||
list_of_flags_with_param = ['o', 'd', 'define', 'b', 'backend', 'cc', 'os', 'target-os', 'cf',
|
list_of_flags_with_param = ['o', 'd', 'define', 'b', 'backend', 'cc', 'os', 'target-os', 'cf',
|
||||||
'cflags', 'path']
|
'cflags', 'path', 'arch']
|
||||||
)
|
)
|
||||||
|
|
||||||
[heap]
|
[heap]
|
||||||
|
@ -54,6 +64,7 @@ pub mut:
|
||||||
os OS // the OS to compile for
|
os OS // the OS to compile for
|
||||||
backend Backend
|
backend Backend
|
||||||
build_mode BuildMode
|
build_mode BuildMode
|
||||||
|
arch Arch
|
||||||
output_mode OutputMode = .stdout
|
output_mode OutputMode = .stdout
|
||||||
// verbosity VerboseLevel
|
// verbosity VerboseLevel
|
||||||
is_verbose bool
|
is_verbose bool
|
||||||
|
@ -162,6 +173,16 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
|
||||||
res.is_apk = true
|
res.is_apk = true
|
||||||
res.build_options << arg
|
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' {
|
'-show-timings' {
|
||||||
res.show_timings = true
|
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) {
|
fn must_exist(path string) {
|
||||||
if !os.exists(path) {
|
if !os.exists(path) {
|
||||||
eprintln('v expects that `$path` exists, but it does not')
|
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
|
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) {
|
fn parse_define(mut prefs Preferences, define string) {
|
||||||
define_parts := define.split('=')
|
define_parts := define.split('=')
|
||||||
prefs.build_options << '-d $define'
|
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') {
|
if !file.ends_with('.v') && !file.ends_with('.vh') {
|
||||||
continue
|
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
|
continue
|
||||||
}
|
}
|
||||||
if prefs.backend == .c && !prefs.should_compile_c(file) {
|
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) {
|
if prefs.backend == .js && !prefs.should_compile_js(file) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if prefs.backend != .js && !prefs.should_compile_asm(file) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if file.contains('_d_') {
|
if file.contains('_d_') {
|
||||||
if prefs.compile_defines_all.len == 0 {
|
if prefs.compile_defines_all.len == 0 {
|
||||||
continue
|
continue
|
||||||
|
@ -99,7 +103,7 @@ fn fname_without_platform_postfix(file string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (prefs &Preferences) should_compile_c(file string) bool {
|
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`.
|
// Probably something like `a.js.v`.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -142,6 +146,24 @@ pub fn (prefs &Preferences) should_compile_c(file string) bool {
|
||||||
return true
|
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 {
|
pub fn (prefs &Preferences) should_compile_js(file string) bool {
|
||||||
if !file.ends_with('.js.v') && file.split('.').len > 2 {
|
if !file.ends_with('.js.v') && file.split('.').len > 2 {
|
||||||
// Probably something like `a.c.v`.
|
// 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
|
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
|
module table
|
||||||
|
|
||||||
import strings
|
import strings
|
||||||
|
import v.pref
|
||||||
|
|
||||||
pub type Type = int
|
pub type Type = int
|
||||||
|
|
||||||
|
@ -22,6 +23,38 @@ pub enum Language {
|
||||||
v
|
v
|
||||||
c
|
c
|
||||||
js
|
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.
|
// 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