mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
native: system for defining internal builtin algorithms which are not inlined (#15105)
This commit is contained in:
parent
58ad6f7999
commit
c73c4dc884
@ -662,20 +662,29 @@ fn (mut g Gen) mov_var_to_reg(reg Register, var Var, config VarConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) call(addr int) {
|
||||
if g.pref.arch == .arm64 {
|
||||
g.bl()
|
||||
return
|
||||
}
|
||||
fn (mut g Gen) call_addr_at(addr int, at i64) i64 {
|
||||
// Need to calculate the difference between current position (position after the e8 call)
|
||||
// and the function to call.
|
||||
// +5 is to get the posistion "e8 xx xx xx xx"
|
||||
// Not sure about the -1.
|
||||
rel := 0xffffffff - (g.buf.len + 5 - addr - 1)
|
||||
return 0xffffffff - (at + 5 - addr - 1)
|
||||
}
|
||||
|
||||
fn (mut g Gen) call(addr int) i64 {
|
||||
if g.pref.arch == .arm64 {
|
||||
g.bl()
|
||||
return 0
|
||||
}
|
||||
|
||||
rel := g.call_addr_at(addr, g.pos())
|
||||
c_addr := g.pos()
|
||||
// println('call addr=$addr.hex2() rel_addr=$rel.hex2() pos=$g.buf.len')
|
||||
g.write8(0xe8)
|
||||
g.write32(rel)
|
||||
|
||||
g.write32(int(rel))
|
||||
g.println('call $addr')
|
||||
|
||||
return c_addr
|
||||
}
|
||||
|
||||
fn (mut g Gen) syscall() {
|
||||
@ -813,8 +822,29 @@ fn (mut g Gen) mul8_var(reg Register, var_offset int) {
|
||||
}
|
||||
|
||||
fn (mut g Gen) leave() {
|
||||
g.write8(0xc9)
|
||||
g.println('leave')
|
||||
g.println('; label 0: return')
|
||||
if g.defer_stmts.len != 0 {
|
||||
// save return value
|
||||
g.push(.rax)
|
||||
for defer_stmt in g.defer_stmts.reverse() {
|
||||
name := '_defer$defer_stmt.idx_in_fn'
|
||||
defer_var := g.get_var_offset(name)
|
||||
g.mov_var_to_reg(.rax, LocalVar{defer_var, ast.i64_type_idx, name})
|
||||
g.cmp_zero(.rax)
|
||||
label := g.labels.new_label()
|
||||
jump_addr := g.cjmp(.je)
|
||||
g.labels.patches << LabelPatch{
|
||||
id: label
|
||||
pos: jump_addr
|
||||
}
|
||||
g.stmts(defer_stmt.stmts)
|
||||
g.labels.addrs[label] = g.pos()
|
||||
}
|
||||
g.pop(.rax)
|
||||
}
|
||||
g.mov_reg(.rsp, .rbp)
|
||||
g.pop(.rbp)
|
||||
g.ret()
|
||||
}
|
||||
|
||||
// returns label's relative address
|
||||
@ -1341,11 +1371,7 @@ fn (mut g Gen) sar8(r Register, val u8) {
|
||||
g.println('sar $r, $val')
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) call_fn(node ast.CallExpr) {
|
||||
if g.pref.arch == .arm64 {
|
||||
g.call_fn_arm64(node)
|
||||
return
|
||||
}
|
||||
pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) {
|
||||
name := node.name
|
||||
mut n := name
|
||||
if !n.contains('.') {
|
||||
@ -1387,6 +1413,12 @@ pub fn (mut g Gen) call_fn(node ast.CallExpr) {
|
||||
g.println('call `${name}()`')
|
||||
}
|
||||
|
||||
fn (mut g Gen) call_builtin_amd64(name string) i64 {
|
||||
call_addr := g.call(0)
|
||||
g.println('call builtin `$name`')
|
||||
return call_addr
|
||||
}
|
||||
|
||||
fn (mut g Gen) patch_calls() {
|
||||
for c in g.callpatches {
|
||||
addr := g.fn_addr[c.name]
|
||||
@ -2083,31 +2115,22 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
|
||||
g.ret()
|
||||
return
|
||||
}
|
||||
// g.leave()
|
||||
g.labels.addrs[0] = g.pos()
|
||||
g.println('; label 0: return')
|
||||
if g.defer_stmts.len != 0 {
|
||||
// save return value
|
||||
g.push(.rax)
|
||||
for defer_stmt in g.defer_stmts.reverse() {
|
||||
name := '_defer$defer_stmt.idx_in_fn'
|
||||
defer_var := g.get_var_offset(name)
|
||||
g.mov_var_to_reg(.rax, LocalVar{defer_var, ast.i64_type_idx, name})
|
||||
g.cmp_zero(.rax)
|
||||
label := g.labels.new_label()
|
||||
jump_addr := g.cjmp(.je)
|
||||
g.labels.patches << LabelPatch{
|
||||
id: label
|
||||
pos: jump_addr
|
||||
}
|
||||
g.stmts(defer_stmt.stmts)
|
||||
g.labels.addrs[label] = g.pos()
|
||||
}
|
||||
g.pop(.rax)
|
||||
}
|
||||
g.mov_reg(.rsp, .rbp)
|
||||
g.pop(.rbp)
|
||||
g.ret()
|
||||
g.leave()
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) builtin_decl_amd64(builtin BuiltinFn) {
|
||||
g.push(.rbp)
|
||||
g.mov_reg(.rbp, .rsp)
|
||||
local_alloc_pos := g.pos()
|
||||
g.sub(.rsp, 0)
|
||||
|
||||
builtin.body(builtin, g)
|
||||
g.println('; stack frame size: $g.stack_var_pos')
|
||||
g.write32_at(local_alloc_pos + 3, g.stack_var_pos)
|
||||
|
||||
g.labels.addrs[0] = g.pos()
|
||||
g.leave()
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2177,9 +2200,13 @@ pub fn (mut g Gen) allocate_var(name string, size int, initial_val int) int {
|
||||
return g.stack_var_pos
|
||||
}
|
||||
|
||||
fn (mut g Gen) convert_int_to_string(r Register, buffer int) {
|
||||
if r != .rax {
|
||||
g.mov_reg(.rax, r)
|
||||
fn (mut g Gen) convert_int_to_string(r1 Register, r2 Register) {
|
||||
if r1 != .rax {
|
||||
g.mov_reg(.rax, r1)
|
||||
}
|
||||
|
||||
if r2 != .rdi {
|
||||
g.mov_reg(.rdi, r2)
|
||||
}
|
||||
|
||||
// check if value in rax is zero
|
||||
@ -2193,7 +2220,13 @@ fn (mut g Gen) convert_int_to_string(r Register, buffer int) {
|
||||
g.println('; jump to label $skip_zero_label')
|
||||
|
||||
// handle zeros seperately
|
||||
g.mov_int_to_var(LocalVar{buffer, ast.u8_type_idx, ''}, '0'[0])
|
||||
// g.mov_int_to_var(LocalVar{buffer, ast.u8_type_idx, ''}, '0'[0])
|
||||
|
||||
g.write8(0xc6)
|
||||
g.write8(0x07)
|
||||
g.write8(0x30)
|
||||
g.println("mov BYTE PTR [rdi], '0'")
|
||||
|
||||
end_label := g.labels.new_label()
|
||||
end_jmp_addr := g.jmp(0)
|
||||
g.labels.patches << LabelPatch{
|
||||
@ -2206,7 +2239,7 @@ fn (mut g Gen) convert_int_to_string(r Register, buffer int) {
|
||||
g.println('; label $skip_zero_label')
|
||||
|
||||
// load a pointer to the string to rdi
|
||||
g.lea_var_to_reg(.rdi, buffer)
|
||||
// g.lea_var_to_reg(.rdi, buffer)
|
||||
|
||||
// detect if value in rax is negative
|
||||
g.cmp_zero(.rax)
|
||||
@ -2219,13 +2252,17 @@ fn (mut g Gen) convert_int_to_string(r Register, buffer int) {
|
||||
g.println('; jump to label $skip_minus_label')
|
||||
|
||||
// add a `-` sign as the first character
|
||||
g.mov_int_to_var(LocalVar{buffer, ast.u8_type_idx, ''}, '-'[0])
|
||||
g.write8(0xc6)
|
||||
g.write8(0x07)
|
||||
g.write8(0x2d)
|
||||
g.println("mov BYTE PTR [rdi], '-'")
|
||||
|
||||
g.neg(.rax) // negate our integer to make it positive
|
||||
g.inc(.rdi) // increment rdi to skip the `-` character
|
||||
g.labels.addrs[skip_minus_label] = g.pos()
|
||||
g.println('; label $skip_minus_label')
|
||||
|
||||
g.mov_reg(.r12, .rdi) // copy the buffer position to rcx
|
||||
g.mov_reg(.r12, .rdi) // copy the buffer position to r12
|
||||
|
||||
loop_label := g.labels.new_label()
|
||||
loop_start := g.pos()
|
||||
@ -2266,7 +2303,9 @@ fn (mut g Gen) convert_int_to_string(r Register, buffer int) {
|
||||
g.labels.addrs[loop_label] = loop_start
|
||||
|
||||
// after all was converted, reverse the string
|
||||
g.reverse_string(.r12)
|
||||
reg := g.get_builtin_arg_reg('reverse_string', 0)
|
||||
g.mov_reg(reg, .r12)
|
||||
g.call_builtin('reverse_string')
|
||||
|
||||
g.labels.addrs[end_label] = g.pos()
|
||||
g.println('; label $end_label')
|
||||
|
81
vlib/v/gen/native/builtins.v
Normal file
81
vlib/v/gen/native/builtins.v
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module native
|
||||
|
||||
import term
|
||||
|
||||
struct BuiltinFn {
|
||||
body fn (builtin BuiltinFn, mut g Gen)
|
||||
arg_regs []Register
|
||||
mut:
|
||||
calls []i64 // call addresses
|
||||
}
|
||||
|
||||
pub const inline_builtins = ['assert', 'print', 'eprint', 'println', 'eprintln', 'exit', 'C.syscall'] // classic V builtin functios accessible to the user get inlined
|
||||
|
||||
pub fn (mut g Gen) init_builtins() {
|
||||
g.builtins = {
|
||||
// longer algorithms and internal functions inaccessible to the user
|
||||
// used to keep executable size small and the bytecode distraction-free
|
||||
'int_to_string': BuiltinFn{
|
||||
body: fn (builtin BuiltinFn, mut g Gen) {
|
||||
g.convert_int_to_string(builtin.arg_regs[0], builtin.arg_regs[1])
|
||||
}
|
||||
arg_regs: [.rcx, .rdi]
|
||||
}
|
||||
'reverse_string': BuiltinFn{
|
||||
body: fn (builtin BuiltinFn, mut g Gen) {
|
||||
g.reverse_string(builtin.arg_regs[0])
|
||||
}
|
||||
arg_regs: [.rdi]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) generate_builtins() {
|
||||
for name, builtin in g.builtins {
|
||||
if builtin.calls.len == 0 { // if a builtin does not get called, do not emit it
|
||||
continue
|
||||
}
|
||||
|
||||
if g.pref.is_verbose {
|
||||
println(term.green('\n(builtin) $name:'))
|
||||
}
|
||||
|
||||
g.stack_var_pos = 0
|
||||
call_addr := g.pos()
|
||||
g.defer_stmts.clear()
|
||||
g.labels = &LabelTable{}
|
||||
|
||||
if g.pref.arch == .arm64 {
|
||||
g.n_error('builtins are not implemented for arm64')
|
||||
} else {
|
||||
g.builtin_decl_amd64(builtin)
|
||||
}
|
||||
|
||||
g.patch_labels()
|
||||
|
||||
// patch all call addresses where this builtin gets called
|
||||
for call in builtin.calls {
|
||||
rel := g.call_addr_at(int(call_addr), call)
|
||||
g.write32_at(call + 1, int(rel))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) get_builtin_arg_reg(name string, index int) Register {
|
||||
builtin := g.builtins[name] or { panic('undefined builtin function $name') }
|
||||
if index >= builtin.arg_regs.len {
|
||||
g.n_error('builtin $name does only have $builtin.arg_regs.len arguments, wanted $index')
|
||||
}
|
||||
return builtin.arg_regs[index]
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) call_builtin(name string) {
|
||||
if g.pref.arch == .arm64 {
|
||||
g.n_error('builtin calls are not implemented for amd64')
|
||||
} else {
|
||||
g.builtins[name].calls << g.call_builtin_amd64(name)
|
||||
}
|
||||
}
|
@ -13,8 +13,6 @@ import v.errors
|
||||
import v.pref
|
||||
import term
|
||||
|
||||
pub const builtins = ['assert', 'print', 'eprint', 'println', 'eprintln', 'exit', 'C.syscall']
|
||||
|
||||
interface CodeGen {
|
||||
mut:
|
||||
g &Gen
|
||||
@ -49,6 +47,7 @@ mut:
|
||||
strs []String
|
||||
labels &LabelTable
|
||||
defer_stmts []ast.DeferStmt
|
||||
builtins map[string]BuiltinFn
|
||||
// macho specific
|
||||
macho_ncmds int
|
||||
macho_cmdsize int
|
||||
@ -194,6 +193,7 @@ pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Pref
|
||||
}
|
||||
g.code_gen.g = g
|
||||
g.generate_header()
|
||||
g.init_builtins()
|
||||
for file in files {
|
||||
/*
|
||||
if file.warnings.len > 0 {
|
||||
@ -205,6 +205,7 @@ pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Pref
|
||||
}
|
||||
g.stmts(file.stmts)
|
||||
}
|
||||
g.generate_builtins()
|
||||
g.generate_footer()
|
||||
return g.nlines, g.buf.len
|
||||
}
|
||||
@ -428,11 +429,26 @@ fn (mut g Gen) gen_typeof_expr(it ast.TypeOf, newline bool) {
|
||||
g.learel(.rax, g.allocate_string('$r$nl', 3, .rel32))
|
||||
}
|
||||
|
||||
fn (mut g Gen) call_fn(node ast.CallExpr) {
|
||||
if g.pref.arch == .arm64 {
|
||||
g.call_fn_arm64(node)
|
||||
} else {
|
||||
g.call_fn_amd64(node)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_var_to_string(reg Register, var Var, config VarConfig) {
|
||||
buffer := g.allocate_array('itoa-buffer', 1, 32) // 32 characters should be enough
|
||||
g.mov_var_to_reg(reg, var, config)
|
||||
g.convert_int_to_string(reg, buffer)
|
||||
g.lea_var_to_reg(reg, buffer)
|
||||
if config.typ.is_int() || config.typ == 0 { // TODO: fix config.typ == 0
|
||||
buffer := g.allocate_array('itoa-buffer', 1, 32) // 32 characters should be enough
|
||||
|
||||
g.mov_var_to_reg(g.get_builtin_arg_reg('int_to_string', 0), var, config)
|
||||
g.lea_var_to_reg(g.get_builtin_arg_reg('int_to_string', 1), buffer)
|
||||
g.call_builtin('int_to_string')
|
||||
|
||||
g.lea_var_to_reg(reg, buffer)
|
||||
} else {
|
||||
g.n_error('int-to-string conversion only implemented for integer types, got $config.typ')
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, name string) {
|
||||
|
Loading…
Reference in New Issue
Block a user