mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 12:03:41 +00:00
Move interface method calls in Go from LLVM IR + documentation
This commit moves the itfmethod call implemented directly in LLVM IR to a Go implementation in the runtime. Additionally, it fixes variable names to be more obvious and adds a lot of documentation to explain how interfaces actually work in TinyGo. Code size changes for src/examples/hello: nrf: -144 unix: -93 I'm guessing this code size reduction is a result of removing the 'noinline' function attribute.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
package runtime
|
||||
|
||||
// This file implements Go interfaces.
|
||||
//
|
||||
// Interfaces are represented as a pair of {typecode, value}, where value can be
|
||||
// anything (including non-pointers).
|
||||
//
|
||||
// Signatures itself are not matched on strings, but on uniqued numbers that
|
||||
// contain the name and the signature of the function (to save space), think of
|
||||
// signatures as interned strings at compile time.
|
||||
//
|
||||
// The typecode is a small number unique for the Go type. All typecodes <
|
||||
// firstInterfaceNum do not have any methods and typecodes >= firstInterfaceNum
|
||||
// all have at least one method. This means that methodSetRanges does not need
|
||||
// to contain types without methods and is thus indexed starting at a typecode
|
||||
// with number firstInterfaceNum.
|
||||
//
|
||||
// To further conserve some space, the methodSetRange (as the name indicates)
|
||||
// doesn't contain a list of methods and function pointers directly, but instead
|
||||
// just indexes into methodSetSignatures and methodSetFunctions which contains
|
||||
// the mapping from uniqued signature to function pointer.
|
||||
|
||||
type _interface struct {
|
||||
typecode uint32
|
||||
value *uint8
|
||||
}
|
||||
|
||||
// This struct indicates the range of methods in the methodSetSignatures and
|
||||
// methodSetFunctions arrays that belong to this named type.
|
||||
type methodSetRange struct {
|
||||
index uint32 // start index into interfaceSignatures and interfaceFunctions
|
||||
length uint32 // number of methods
|
||||
}
|
||||
|
||||
// Global constants that will be set by the compiler. The arrays are of size 0,
|
||||
// which is a dummy value, but will be bigger after the compiler has filled them
|
||||
// in.
|
||||
var (
|
||||
methodSetRanges [0]methodSetRange // indexes into methodSetSignatures and methodSetFunctions
|
||||
methodSetSignatures [0]uint32 // uniqued method ID
|
||||
methodSetFunctions [0]*uint8 // function pointer of method
|
||||
firstInterfaceNum uint32 // the lowest typecode that has at least one method
|
||||
)
|
||||
|
||||
// Get the function pointer for the method on the interface.
|
||||
// This is a compiler intrinsic.
|
||||
func itfmethod(itf _interface, method uint32) *uint8 {
|
||||
// This function doesn't do bounds checking as the supplied method must be
|
||||
// in the list of signatures. The compiler will only emit runtime.itfmethod
|
||||
// calls when the method actually exists on this interface (proven by the
|
||||
// typechecker).
|
||||
i := methodSetRanges[itf.typecode-firstInterfaceNum].index
|
||||
for {
|
||||
if methodSetSignatures[i] == method {
|
||||
return methodSetFunctions[i]
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
source_filename = "runtime/runtime.ll"
|
||||
|
||||
%interface = type { i32, i8* }
|
||||
|
||||
declare void @runtime.initAll()
|
||||
declare void @main.main()
|
||||
declare i8* @main.main$async(i8*)
|
||||
@@ -10,15 +8,6 @@ declare void @runtime.scheduler(i8*)
|
||||
; Will be changed to true if there are 'go' statements in the compiled program.
|
||||
@has_scheduler = private unnamed_addr constant i1 false
|
||||
|
||||
; Will be changed by the compiler to the first type number with methods.
|
||||
@first_interface_num = private unnamed_addr constant i32 0
|
||||
|
||||
; Will be filled by the compiler with runtime type information.
|
||||
%interface_tuple = type { i32, i32 } ; { index, len }
|
||||
@interface_tuples = external global [0 x %interface_tuple]
|
||||
@interface_signatures = external global [0 x i32] ; array of method IDs
|
||||
@interface_functions = external global [0 x i8*] ; array of function pointers
|
||||
|
||||
define i32 @main() {
|
||||
call void @runtime.initAll()
|
||||
%has_scheduler = load i1, i1* @has_scheduler
|
||||
@@ -36,34 +25,3 @@ without_scheduler:
|
||||
call void @main.main()
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
; Get the function pointer for the method on the interface.
|
||||
; This function only reads constant global data and it's own arguments so it can
|
||||
; be 'readnone' (a pure function).
|
||||
define i8* @itfmethod(%interface %itf, i32 %method) noinline readnone {
|
||||
entry:
|
||||
; Calculate the index in @interface_tuples
|
||||
%concrete_type_num = extractvalue %interface %itf, 0
|
||||
%first_interface_num = load i32, i32* @first_interface_num
|
||||
%index = sub i32 %concrete_type_num, %first_interface_num
|
||||
|
||||
; Calculate the index for @interface_signatures and @interface_functions
|
||||
%itf_index_ptr = getelementptr inbounds [0 x %interface_tuple], [0 x %interface_tuple]* @interface_tuples, i32 0, i32 %index, i32 0
|
||||
%itf_index = load i32, i32* %itf_index_ptr
|
||||
br label %find_method
|
||||
|
||||
; This is a while loop until the method has been found.
|
||||
; It must be in here, so avoid checking the length.
|
||||
find_method:
|
||||
%itf_index.phi = phi i32 [ %itf_index, %entry], [ %itf_index.phi.next, %find_method]
|
||||
%m_ptr = getelementptr inbounds [0 x i32], [0 x i32]* @interface_signatures, i32 0, i32 %itf_index.phi
|
||||
%m = load i32, i32* %m_ptr
|
||||
%found = icmp eq i32 %m, %method
|
||||
%itf_index.phi.next = add i32 %itf_index.phi, 1
|
||||
br i1 %found, label %found_method, label %find_method
|
||||
|
||||
found_method:
|
||||
%fp_ptr = getelementptr inbounds [0 x i8*], [0 x i8*]* @interface_functions, i32 0, i32 %itf_index.phi
|
||||
%fp = load i8*, i8** %fp_ptr
|
||||
ret i8* %fp
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user