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) {
|
fn (mut g Gen) call_addr_at(addr int, at i64) i64 {
|
||||||
if g.pref.arch == .arm64 {
|
|
||||||
g.bl()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Need to calculate the difference between current position (position after the e8 call)
|
// Need to calculate the difference between current position (position after the e8 call)
|
||||||
// and the function to call.
|
// and the function to call.
|
||||||
// +5 is to get the posistion "e8 xx xx xx xx"
|
// +5 is to get the posistion "e8 xx xx xx xx"
|
||||||
// Not sure about the -1.
|
// 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')
|
// println('call addr=$addr.hex2() rel_addr=$rel.hex2() pos=$g.buf.len')
|
||||||
g.write8(0xe8)
|
g.write8(0xe8)
|
||||||
g.write32(rel)
|
|
||||||
|
g.write32(int(rel))
|
||||||
g.println('call $addr')
|
g.println('call $addr')
|
||||||
|
|
||||||
|
return c_addr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) syscall() {
|
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() {
|
fn (mut g Gen) leave() {
|
||||||
g.write8(0xc9)
|
g.println('; label 0: return')
|
||||||
g.println('leave')
|
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
|
// returns label's relative address
|
||||||
@ -1341,11 +1371,7 @@ fn (mut g Gen) sar8(r Register, val u8) {
|
|||||||
g.println('sar $r, $val')
|
g.println('sar $r, $val')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) call_fn(node ast.CallExpr) {
|
pub fn (mut g Gen) call_fn_amd64(node ast.CallExpr) {
|
||||||
if g.pref.arch == .arm64 {
|
|
||||||
g.call_fn_arm64(node)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name := node.name
|
name := node.name
|
||||||
mut n := name
|
mut n := name
|
||||||
if !n.contains('.') {
|
if !n.contains('.') {
|
||||||
@ -1387,6 +1413,12 @@ pub fn (mut g Gen) call_fn(node ast.CallExpr) {
|
|||||||
g.println('call `${name}()`')
|
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() {
|
fn (mut g Gen) patch_calls() {
|
||||||
for c in g.callpatches {
|
for c in g.callpatches {
|
||||||
addr := g.fn_addr[c.name]
|
addr := g.fn_addr[c.name]
|
||||||
@ -2083,31 +2115,22 @@ fn (mut g Gen) fn_decl_amd64(node ast.FnDecl) {
|
|||||||
g.ret()
|
g.ret()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// g.leave()
|
|
||||||
g.labels.addrs[0] = g.pos()
|
g.labels.addrs[0] = g.pos()
|
||||||
g.println('; label 0: return')
|
g.leave()
|
||||||
if g.defer_stmts.len != 0 {
|
}
|
||||||
// save return value
|
|
||||||
g.push(.rax)
|
pub fn (mut g Gen) builtin_decl_amd64(builtin BuiltinFn) {
|
||||||
for defer_stmt in g.defer_stmts.reverse() {
|
g.push(.rbp)
|
||||||
name := '_defer$defer_stmt.idx_in_fn'
|
g.mov_reg(.rbp, .rsp)
|
||||||
defer_var := g.get_var_offset(name)
|
local_alloc_pos := g.pos()
|
||||||
g.mov_var_to_reg(.rax, LocalVar{defer_var, ast.i64_type_idx, name})
|
g.sub(.rsp, 0)
|
||||||
g.cmp_zero(.rax)
|
|
||||||
label := g.labels.new_label()
|
builtin.body(builtin, g)
|
||||||
jump_addr := g.cjmp(.je)
|
g.println('; stack frame size: $g.stack_var_pos')
|
||||||
g.labels.patches << LabelPatch{
|
g.write32_at(local_alloc_pos + 3, g.stack_var_pos)
|
||||||
id: label
|
|
||||||
pos: jump_addr
|
g.labels.addrs[0] = g.pos()
|
||||||
}
|
g.leave()
|
||||||
g.stmts(defer_stmt.stmts)
|
|
||||||
g.labels.addrs[label] = g.pos()
|
|
||||||
}
|
|
||||||
g.pop(.rax)
|
|
||||||
}
|
|
||||||
g.mov_reg(.rsp, .rbp)
|
|
||||||
g.pop(.rbp)
|
|
||||||
g.ret()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2177,9 +2200,13 @@ pub fn (mut g Gen) allocate_var(name string, size int, initial_val int) int {
|
|||||||
return g.stack_var_pos
|
return g.stack_var_pos
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) convert_int_to_string(r Register, buffer int) {
|
fn (mut g Gen) convert_int_to_string(r1 Register, r2 Register) {
|
||||||
if r != .rax {
|
if r1 != .rax {
|
||||||
g.mov_reg(.rax, r)
|
g.mov_reg(.rax, r1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r2 != .rdi {
|
||||||
|
g.mov_reg(.rdi, r2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if value in rax is zero
|
// 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')
|
g.println('; jump to label $skip_zero_label')
|
||||||
|
|
||||||
// handle zeros seperately
|
// 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_label := g.labels.new_label()
|
||||||
end_jmp_addr := g.jmp(0)
|
end_jmp_addr := g.jmp(0)
|
||||||
g.labels.patches << LabelPatch{
|
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')
|
g.println('; label $skip_zero_label')
|
||||||
|
|
||||||
// load a pointer to the string to rdi
|
// 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
|
// detect if value in rax is negative
|
||||||
g.cmp_zero(.rax)
|
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')
|
g.println('; jump to label $skip_minus_label')
|
||||||
|
|
||||||
// add a `-` sign as the first character
|
// 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.neg(.rax) // negate our integer to make it positive
|
||||||
g.inc(.rdi) // increment rdi to skip the `-` character
|
g.inc(.rdi) // increment rdi to skip the `-` character
|
||||||
g.labels.addrs[skip_minus_label] = g.pos()
|
g.labels.addrs[skip_minus_label] = g.pos()
|
||||||
g.println('; label $skip_minus_label')
|
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_label := g.labels.new_label()
|
||||||
loop_start := g.pos()
|
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
|
g.labels.addrs[loop_label] = loop_start
|
||||||
|
|
||||||
// after all was converted, reverse the string
|
// 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.labels.addrs[end_label] = g.pos()
|
||||||
g.println('; label $end_label')
|
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 v.pref
|
||||||
import term
|
import term
|
||||||
|
|
||||||
pub const builtins = ['assert', 'print', 'eprint', 'println', 'eprintln', 'exit', 'C.syscall']
|
|
||||||
|
|
||||||
interface CodeGen {
|
interface CodeGen {
|
||||||
mut:
|
mut:
|
||||||
g &Gen
|
g &Gen
|
||||||
@ -49,6 +47,7 @@ mut:
|
|||||||
strs []String
|
strs []String
|
||||||
labels &LabelTable
|
labels &LabelTable
|
||||||
defer_stmts []ast.DeferStmt
|
defer_stmts []ast.DeferStmt
|
||||||
|
builtins map[string]BuiltinFn
|
||||||
// macho specific
|
// macho specific
|
||||||
macho_ncmds int
|
macho_ncmds int
|
||||||
macho_cmdsize 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.code_gen.g = g
|
||||||
g.generate_header()
|
g.generate_header()
|
||||||
|
g.init_builtins()
|
||||||
for file in files {
|
for file in files {
|
||||||
/*
|
/*
|
||||||
if file.warnings.len > 0 {
|
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.stmts(file.stmts)
|
||||||
}
|
}
|
||||||
|
g.generate_builtins()
|
||||||
g.generate_footer()
|
g.generate_footer()
|
||||||
return g.nlines, g.buf.len
|
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))
|
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) {
|
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
|
if config.typ.is_int() || config.typ == 0 { // TODO: fix config.typ == 0
|
||||||
g.mov_var_to_reg(reg, var, config)
|
buffer := g.allocate_array('itoa-buffer', 1, 32) // 32 characters should be enough
|
||||||
g.convert_int_to_string(reg, buffer)
|
|
||||||
g.lea_var_to_reg(reg, buffer)
|
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) {
|
pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, name string) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user