Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cgo: add support for printf #4101

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@mkdir -p build/release/tinygo/lib/musl/arch
@mkdir -p build/release/tinygo/lib/musl/crt
Expand Down Expand Up @@ -840,10 +841,12 @@ endif
@cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/legacy build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/locale build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/linux build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/math build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/multibyte build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src
@cp -rp lib/musl/src/string build/release/tinygo/lib/musl/src
Expand All @@ -853,6 +856,7 @@ endif
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio/ucrt_* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
Expand Down
5 changes: 3 additions & 2 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
libcDependencies = append(libcDependencies, dummyCompileJob(path))
case "mingw-w64":
_, unlock, err := MinGW.load(config, tmpdir)
job, unlock, err := MinGW.load(config, tmpdir)
if err != nil {
return BuildResult{}, err
}
unlock()
defer unlock()
libcDependencies = append(libcDependencies, job)
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...)
case "":
// no library specified, so nothing to do
Expand Down
41 changes: 34 additions & 7 deletions builder/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import (
"github.com/tinygo-org/tinygo/goenv"
)

// These are the GENERIC_SOURCES according to CMakeList.txt.
var genericBuiltins = []string{
// These are the GENERIC_SOURCES according to CMakeList.txt as of LLVM 17.
"absvdi2.c",
"absvsi2.c",
"absvti2.c",
"adddf3.c",
"addsf3.c",
"addtf3.c",
"addvdi3.c",
"addvsi3.c",
"addvti3.c",
Expand All @@ -40,12 +39,12 @@ var genericBuiltins = []string{
"divdf3.c",
"divdi3.c",
"divmoddi4.c",
//"divmodsi4.c", // see aeabiBuiltins
"divmodti4.c",
"divsc3.c",
"divsf3.c",
"divsi3.c",
"divtc3.c",
"divti3.c",
"divtf3.c",
"extendsfdf2.c",
"extendhfsf2.c",
"ffsdi2.c",
Expand Down Expand Up @@ -91,7 +90,6 @@ var genericBuiltins = []string{
"mulsc3.c",
"mulsf3.c",
"multi3.c",
"multf3.c",
"mulvdi3.c",
"mulvsi3.c",
"mulvti3.c",
Expand All @@ -111,13 +109,11 @@ var genericBuiltins = []string{
"popcountti2.c",
"powidf2.c",
"powisf2.c",
"powitf2.c",
"subdf3.c",
"subsf3.c",
"subvdi3.c",
"subvsi3.c",
"subvti3.c",
"subtf3.c",
"trampoline_setup.c",
"truncdfhf2.c",
"truncdfsf2.c",
Expand All @@ -126,12 +122,43 @@ var genericBuiltins = []string{
"ucmpti2.c",
"udivdi3.c",
"udivmoddi4.c",
//"udivmodsi4.c", // see aeabiBuiltins
"udivmodti4.c",
"udivsi3.c",
"udivti3.c",
"umoddi3.c",
"umodsi3.c",
"umodti3.c",

// These are the GENERIC_TF_SOURCES as of LLVM 17.
// They are not needed on all platforms (32-bit platforms usually don't need
// these) but they seem to compile fine so it's easier to include them.
"addtf3.c",
"comparetf2.c",
"divtc3.c",
"divtf3.c",
"extenddftf2.c",
"extendhftf2.c",
"extendsftf2.c",
"fixtfdi.c",
"fixtfsi.c",
"fixtfti.c",
"fixunstfdi.c",
"fixunstfsi.c",
"fixunstfti.c",
"floatditf.c",
"floatsitf.c",
"floattitf.c",
"floatunditf.c",
"floatunsitf.c",
"floatuntitf.c",
"multc3.c",
"multf3.c",
"powitf2.c",
"subtf3.c",
"trunctfdf2.c",
"trunctfhf2.c",
"trunctfsf2.c",
}

var aeabiBuiltins = []string{
Expand Down
26 changes: 21 additions & 5 deletions builder/mingw-w64.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,30 @@ var MinGW = Library{
_, err = io.Copy(outf, inf)
return err
},
sourceDir: func() string { return "" }, // unused
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") },
cflags: func(target, headerPath string) []string {
// No flags necessary because there are no files to compile.
return nil
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
return []string{
"-nostdlibinc",
"-isystem", mingwDir + "/mingw-w64-headers/crt",
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
"-I" + headerPath,
}
},
librarySources: func(target string) ([]string, error) {
// We only use the UCRT DLL file. No source files necessary.
return nil, nil
// These files are needed so that printf and the like are supported.
sources := []string{
"mingw-w64-crt/stdio/ucrt_fprintf.c",
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
"mingw-w64-crt/stdio/ucrt_printf.c",
"mingw-w64-crt/stdio/ucrt_snprintf.c",
"mingw-w64-crt/stdio/ucrt_sprintf.c",
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
"mingw-w64-crt/stdio/ucrt_vprintf.c",
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
}
return sources, nil
},
}

Expand Down
4 changes: 4 additions & 0 deletions builder/musl.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ var Musl = Library{
"-Wno-ignored-pragmas",
"-Wno-tautological-constant-out-of-range-compare",
"-Wno-deprecated-non-prototype",
"-Wno-format",
"-Wno-parentheses",
"-Qunused-arguments",
// Select include dirs. Don't include standard library includes
// (that would introduce host dependencies and other complications),
Expand Down Expand Up @@ -119,11 +121,13 @@ var Musl = Library{
"internal/syscall_ret.c",
"internal/vdso.c",
"legacy/*.c",
"locale/*.c",
"linux/*.c",
"malloc/*.c",
"malloc/mallocng/*.c",
"mman/*.c",
"math/*.c",
"multibyte/*.c",
"signal/*.c",
"stdio/*.c",
"string/*.c",
Expand Down
1 change: 1 addition & 0 deletions builder/picolibc.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var Picolibc = Library{
"-D_HAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO",
"-DPOSIX_IO",
"-DFORMAT_DEFAULT_INTEGER", // use __i_vfprintf and __i_vfscanf by default
"-D_IEEE_LIBM",
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
"-D__OBSOLETE_MATH_DOUBLE=0",
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/runtime_tinygowasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ func fd_write(id uint32, iovs *__wasi_iovec_t, iovs_len uint, nwritten *uint) (e
//go:wasmimport wasi_snapshot_preview1 proc_exit
func proc_exit(exitcode uint32)

// Flush stdio on exit.
//
//export __stdio_exit
func __stdio_exit()

const (
putcharBufferSize = 120
stdout = 1
Expand Down Expand Up @@ -72,6 +77,9 @@ func abort() {

//go:linkname syscall_Exit syscall.Exit
func syscall_Exit(code int) {
// TODO: should we call __stdio_exit here?
// It's a low-level exit (syscall.Exit) so doing any libc stuff seems
// unexpected, but then where else should stdio buffers be flushed?
proc_exit(uint32(code))
}

Expand Down
1 change: 1 addition & 0 deletions src/runtime/runtime_wasm_js.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func _start() {

wasmNested = true
run()
__stdio_exit()
wasmNested = false
}

Expand Down
1 change: 1 addition & 0 deletions src/runtime/runtime_wasm_wasi.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func _start() {
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
run()
__stdio_exit()
}

// Read the command line arguments from WASI.
Expand Down
5 changes: 5 additions & 0 deletions testdata/cgo/main.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "main.h"
#include <stdio.h>

int global = 3;
bool globalBool = 1;
Expand Down Expand Up @@ -67,3 +68,7 @@ void unionSetData(short f0, short f1, short f2) {
void arraydecay(int buf1[5], int buf2[3][8], int buf3[4][7][2]) {
// Do nothing.
}

void printf_single_int(char *format, int arg) {
printf(format, arg);
}
4 changes: 4 additions & 0 deletions testdata/cgo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ func main() {
// libc: test basic stdio functionality
putsBuf := []byte("line written using C puts\x00")
C.puts((*C.char)(unsafe.Pointer(&putsBuf[0])))

// libc: test whether printf works in C.
printfBuf := []byte("line written using C printf with value=%d\n\x00")
C.printf_single_int((*C.char)(unsafe.Pointer(&printfBuf[0])), -21)
}

func printUnion(union C.joined_t) C.joined_t {
Expand Down
2 changes: 2 additions & 0 deletions testdata/cgo/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,5 @@ extern int global;
// Test array decaying into a pointer.
typedef int arraydecay_buf3[4][7][2];
void arraydecay(int buf1[5], int buf2[3][8], arraydecay_buf3 buf3);

void printf_single_int(char *format, int arg);
1 change: 1 addition & 0 deletions testdata/cgo/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ len(C.GoStringN(nil, 0)): 0
len(C.GoBytes(nil, 0)): 0
copied string: foobar
line written using C puts
line written using C printf with value=-21