Skip to content

Commit d824ca8

Browse files
authored
Merge pull request #483 from savi-lang/add/ffi-global-cpointer
Add `:ffi global cpointer` declarator to give the address of a C global.
2 parents c2e2c3c + c1b560e commit d824ca8

File tree

12 files changed

+166
-40
lines changed

12 files changed

+166
-40
lines changed

core/declarators/declarators.savi

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,10 @@
453453
:: Use `var` if the global variable is expected to change (either on the Savi
454454
:: side or on the C side), or `let` if it's expected to be set just once.
455455
::
456+
:: Alternatively, `cpointer` can be specified instead of `var` or `let`,
457+
:: causing the declaration to generate a getter that returns the address of the
458+
:: global variable as a `CPointer(T)`, where `T` is the specified `type`.
459+
::
456460
:: WARNING: Savi programs involve actors and are implicitly multi-threaded,
457461
:: so global variables whose values are expected to change during the life
458462
:: of the program are very likely to cause memory safety issues.
@@ -464,7 +468,7 @@
464468
:begins ffi
465469

466470
:keyword global
467-
:term var_or_let enum (var, let)
471+
:term var_or_let enum (var, let, cpointer)
468472
:term name Name
469473
:term type Type
470474

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:manifest "example"
2+
:sources "src/*.savi"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
initially...
2+
foo == 1 == 1
3+
bar == 2
4+
foo cpointer and foo_2 cpointer have the same address? True
5+
foo cpointer and bar cpointer have the same address? False
6+
---
7+
foo = 42 returns 42
8+
foo == 42 == 42
9+
bar == 2
10+
---
11+
bar = 99 returns 99
12+
foo == 42 == 42
13+
bar == 99
14+
---
15+
setting foo via cpointer to 32
16+
foo == 32 == 32
17+
bar == 99
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
$SAVI
2+
bin/example
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
2+
:ffi_link_c_files (
3+
"../vendor/mylib_globals.c"
4+
)
5+
6+
:module _FFI.Cast(A, B)
7+
:ffi pointer(input A) B
8+
:foreign_name savi_cast_pointer
9+
10+
:module _FFI.Globals
11+
:ffi global var foo U64
12+
:ffi global var bar U64
13+
14+
:ffi global let foo_2 U64
15+
:foreign_name foo
16+
17+
:ffi global cpointer foo_cpointer U64
18+
:foreign_name foo
19+
:ffi global cpointer bar_cpointer U64
20+
:foreign_name bar
21+
:ffi global cpointer foo_cpointer_2 U64
22+
:foreign_name foo
23+
24+
:fun "foo_via_cpointer="(value U64)
25+
cpointer = _FFI.Globals.foo_cpointer
26+
array = Array(U64).from_cpointer(
27+
_FFI.Cast(CPointer(U64), CPointer(U64)'ref).pointer(cpointer)
28+
1
29+
1
30+
)
31+
try (array[0]! = value)
32+
value
33+
34+
:actor Main
35+
:new (env)
36+
env.out.print("initially...")
37+
env.out.print("foo == \(_FFI.Globals.foo) == \(_FFI.Globals.foo_2)")
38+
env.out.print("bar == \(_FFI.Globals.bar)")
39+
env.out.print("foo cpointer and foo_2 cpointer have the same address? \(
40+
_FFI.Globals.foo_cpointer.address == _FFI.Globals.foo_cpointer_2.address
41+
)")
42+
env.out.print("foo cpointer and bar cpointer have the same address? \(
43+
_FFI.Globals.foo_cpointer.address == _FFI.Globals.bar_cpointer.address
44+
)")
45+
env.out.print("---")
46+
env.out.print("foo = 42 returns \(_FFI.Globals.foo = 42)")
47+
env.out.print("foo == \(_FFI.Globals.foo) == \(_FFI.Globals.foo_2)")
48+
env.out.print("bar == \(_FFI.Globals.bar)")
49+
env.out.print("---")
50+
env.out.print("bar = 99 returns \(_FFI.Globals.bar = 99)")
51+
env.out.print("foo == \(_FFI.Globals.foo) == \(_FFI.Globals.foo_2)")
52+
env.out.print("bar == \(_FFI.Globals.bar)")
53+
env.out.print("---")
54+
env.out.print("setting foo via cpointer to \(_FFI.Globals.foo_via_cpointer = 32)")
55+
env.out.print("foo == \(_FFI.Globals.foo) == \(_FFI.Globals.foo_2)")
56+
env.out.print("bar == \(_FFI.Globals.bar)")
57+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#include <stdint.h>
2+
3+
uint64_t foo = 1;
4+
uint64_t bar = 2;
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
2 + 2 == 4!
22
4 - 2 == 2!
3-
the magic number is 3
4-
the other magic number is 4.2

spec/integration/run-ffi-link-c-files/src/Main.savi

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,13 @@
22
:ffi_link_c_files (
33
"../vendor/mylib_add.c"
44
"../vendor/mylib_sub.c"
5-
"../vendor/mylib_magic_number.c"
65
)
76

87
:module _FFI
98
:ffi mylib_add(a I32, b I32) I32
109
:ffi mylib_sub(a I32, b I32) I32
11-
:ffi global let mylib_magic_number I32
12-
:ffi global let mylib_other_magic_number F64
13-
:foreign_name mylib_magic_number_2
1410

1511
:actor Main
1612
:new (env)
1713
env.out.print("2 + 2 == \(_FFI.mylib_add(2, 2))!")
1814
env.out.print("4 - 2 == \(_FFI.mylib_sub(4, 2))!")
19-
env.out.print("the magic number is \(_FFI.mylib_magic_number)")
20-
env.out.print("the other magic number is \(_FFI.mylib_other_magic_number)")

spec/integration/run-ffi-link-c-files/vendor/mylib_magic_number.c

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/savi/compiler/code_gen.cr

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,9 @@ class Savi::Compiler::CodeGen
819819
return gen_intrinsic(gtype, gfunc, llvm_func) if gfunc.func.has_tag?(:compiler_intrinsic)
820820

821821
if gfunc.func.has_tag?(:ffi)
822-
if gfunc.func.has_tag?(:ffi_global_getter)
822+
if gfunc.func.has_tag?(:ffi_global_cpointer_getter)
823+
return gen_ffi_global_cpointer_getter_impl(gtype, gfunc, llvm_func)
824+
elsif gfunc.func.has_tag?(:ffi_global_getter)
823825
return gen_ffi_global_getter_impl(gtype, gfunc, llvm_func)
824826
elsif gfunc.func.has_tag?(:ffi_global_setter)
825827
return gen_ffi_global_setter_impl(gtype, gfunc, llvm_func)
@@ -967,15 +969,44 @@ class Savi::Compiler::CodeGen
967969
end
968970

969971
def gen_ffi_global_decl(gfunc)
970-
llvm_type = llvm_type_of(gfunc.func.ret.not_nil!, gfunc)
972+
global_name = gfunc.func.metadata[:ffi_link_name].as(String)
973+
974+
# Determine the type of the global variable value.
975+
# If this is a cpointer getter we need to unwrap its type arg.
976+
t = type_of(gfunc.func.ret.not_nil!, gfunc)
977+
if gfunc.func.has_tag?(:ffi_global_cpointer_getter)
978+
t = gtype_of(t).type_def.cpointer_type_arg(ctx)
979+
end
980+
llvm_type = llvm_type_of(t)
981+
982+
# If the global already exists, check that it has a compatible llvm_type
983+
# or give a compilation error, but always return the existing global,
984+
# rather than creating another global with an implicitly incremented name.
985+
existing = @mod.globals[global_name]?
986+
if existing
987+
existing_llvm_type = LLVM::Type.new(LibLLVM.global_get_value_type(existing))
988+
raise Error.at gfunc.func.ident.pos, "FFI global #{global_name} already exists with a different type" \
989+
unless existing_llvm_type.kind == llvm_type.kind \
990+
&& abi_size_of(existing_llvm_type) == abi_size_of(llvm_type)
991+
return existing
992+
end
971993

972-
global = @mod.globals.add(llvm_type, gfunc.func.metadata[:ffi_link_name].as(String))
994+
global = @mod.globals.add(llvm_type, global_name)
973995
global.linkage = LLVM::Linkage::External
974996
global.global_constant = gfunc.func.has_tag?(:ffi_global_constant)
975997
global.externally_initialized = true # TODO: false and set an initializer if this ffi var has an initializer
976998
global
977999
end
9781000

1001+
def gen_ffi_global_cpointer_getter_impl(gtype, gfunc, llvm_func)
1002+
gen_func_start(llvm_func, gtype, gfunc)
1003+
1004+
global = gen_ffi_global_decl(gfunc)
1005+
@builder.ret(global)
1006+
1007+
gen_func_end(gfunc)
1008+
end
1009+
9791010
def gen_ffi_global_getter_impl(gtype, gfunc, llvm_func)
9801011
gen_func_start(llvm_func, gtype, gfunc)
9811012

@@ -992,8 +1023,7 @@ class Savi::Compiler::CodeGen
9921023
def gen_ffi_global_setter_impl(gtype, gfunc, llvm_func)
9931024
gen_func_start(llvm_func, gtype, gfunc)
9941025

995-
global = @mod.globals[gfunc.func.metadata[:ffi_link_name]]
996-
1026+
global = gen_ffi_global_decl(gfunc)
9971027
value = llvm_func.params[0]
9981028
@builder.store(value, global)
9991029

0 commit comments

Comments
 (0)