Merge commit '6588f36123eababf6e24564b49e5af374285d2b5' into optional-esp-clang
# Conflicts: # internal/crosscompile/crosscompile.go # internal/crosscompile/crosscompile_test.go
This commit is contained in:
21
_demo/asmfullcall/asmfullcall.go
Normal file
21
_demo/asmfullcall/asmfullcall.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//llgo:link asmFull llgo.asm
|
||||
func asmFull(instruction string, regs map[string]any) uintptr { return 0 }
|
||||
|
||||
var testVar = 0
|
||||
|
||||
func main() {
|
||||
verify()
|
||||
}
|
||||
|
||||
func check(expected, actual int) {
|
||||
if expected != actual {
|
||||
panic(fmt.Sprintf("Expected: %d, Got: %d\n", expected, actual))
|
||||
}
|
||||
fmt.Println("asm check passed:", actual)
|
||||
}
|
||||
31
_demo/asmfullcall/asmfullcall_darwin.go
Normal file
31
_demo/asmfullcall/asmfullcall_darwin.go
Normal file
@@ -0,0 +1,31 @@
|
||||
//go:build darwin && arm64
|
||||
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func verify() {
|
||||
// 0 output & 0 input
|
||||
asmFull("nop", nil)
|
||||
|
||||
// 0 output & 1 input with memory address
|
||||
addr := uintptr(unsafe.Pointer(&testVar))
|
||||
asmFull("str {value}, [{addr}]", map[string]any{
|
||||
"addr": addr,
|
||||
"value": 43,
|
||||
})
|
||||
check(43, testVar)
|
||||
|
||||
// 1 output & 1 input
|
||||
res1 := asmFull("mov {}, {value}", map[string]any{
|
||||
"value": 41,
|
||||
})
|
||||
check(41, int(res1))
|
||||
|
||||
// 1 output & 2 inputs
|
||||
res2 := asmFull("add {}, {a}, {b}", map[string]any{
|
||||
"a": 25,
|
||||
"b": 17,
|
||||
})
|
||||
check(42, int(res2))
|
||||
}
|
||||
30
_demo/asmfullcall/asmfullcall_linux.go
Normal file
30
_demo/asmfullcall/asmfullcall_linux.go
Normal file
@@ -0,0 +1,30 @@
|
||||
//go:build linux && amd64
|
||||
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func verify() {
|
||||
// 0 output & 0 input
|
||||
asmFull("nop", nil)
|
||||
|
||||
// 0 output & 1 input with memory address
|
||||
addr := uintptr(unsafe.Pointer(&testVar))
|
||||
asmFull("movq {value}, ({addr})", map[string]any{
|
||||
"addr": addr,
|
||||
"value": 43,
|
||||
})
|
||||
check(43, testVar)
|
||||
|
||||
// 1 output & 1 input
|
||||
res1 := asmFull("movq {value}, {}", map[string]any{
|
||||
"value": 41,
|
||||
})
|
||||
check(41, int(res1))
|
||||
|
||||
res2 := asmFull("leaq ({a},{b}), {}", map[string]any{
|
||||
"a": 25,
|
||||
"b": 17,
|
||||
})
|
||||
check(42, int(res2))
|
||||
}
|
||||
@@ -13,7 +13,17 @@ func main() {
|
||||
if len(entries) == 0 {
|
||||
panic("No files found")
|
||||
}
|
||||
var check int
|
||||
for _, e := range entries {
|
||||
fmt.Printf("%s isDir[%t]\n", e.Name(), e.IsDir())
|
||||
if !e.IsDir() {
|
||||
switch e.Name() {
|
||||
case "go.sum", "go.mod":
|
||||
check++
|
||||
}
|
||||
}
|
||||
}
|
||||
if check != 2 {
|
||||
panic("Bad readdir entries go.mod/go.sum")
|
||||
}
|
||||
}
|
||||
|
||||
16
_embdemo/hello-esp32/main.go
Normal file
16
_embdemo/hello-esp32/main.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import "github.com/goplus/lib/c"
|
||||
|
||||
func myprint(s *c.Char) {
|
||||
for i := 0; i < int(c.Strlen(s)); i++ {
|
||||
WriteByte(byte(c.Index(s, i)))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
for {
|
||||
myprint(c.Str("hello world"))
|
||||
sleep(1)
|
||||
}
|
||||
}
|
||||
13
_embdemo/hello-esp32/uart.go
Normal file
13
_embdemo/hello-esp32/uart.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/lib/c"
|
||||
)
|
||||
|
||||
//go:linkname WriteByte C.board_uart_write_char
|
||||
func WriteByte(b byte)
|
||||
|
||||
//go:linkname sleep sleep
|
||||
func sleep(c c.Int)
|
||||
31
_embdemo/write-esp32/main.go
Normal file
31
_embdemo/write-esp32/main.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/lib/c"
|
||||
)
|
||||
|
||||
//go:linkname write C.write
|
||||
func write(c.Int, *c.Char, c.SizeT) int
|
||||
|
||||
func main() {
|
||||
buf := c.Malloc(6)
|
||||
c.Memset(buf, 0, 6)
|
||||
c.Strncpy((*c.Char)(buf), c.Str("abcde"), 5)
|
||||
|
||||
if c.Strcmp((*c.Char)(buf), c.Str("abcde")) == 0 {
|
||||
write(1, c.Str("pass strcmp"), 11)
|
||||
}
|
||||
|
||||
if byte(c.Index((*c.Char)(buf), 0)) == 'a' {
|
||||
write(1, c.Str("pass index"), 10)
|
||||
}
|
||||
|
||||
c.Memset(buf, c.Int('A'), 5)
|
||||
if c.Strcmp((*c.Char)(buf), c.Str("AAAAA")) == 0 {
|
||||
write(1, c.Str("pass memeset"), 11)
|
||||
}
|
||||
|
||||
write(1, (*c.Char)(buf), 5)
|
||||
}
|
||||
27
cl/_testrt/asmfull/in.go
Normal file
27
cl/_testrt/asmfull/in.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
//go:linkname asmFull llgo.asm
|
||||
func asmFull(instruction string, regs map[string]any) uintptr
|
||||
|
||||
func main() {
|
||||
// no input,no return value
|
||||
asmFull("nop", nil)
|
||||
// input only,no return value
|
||||
asmFull("# test value {value}", map[string]any{"value": 42})
|
||||
// input with return value
|
||||
res1 := asmFull("mov {}, {value}", map[string]any{
|
||||
"value": 42,
|
||||
})
|
||||
println("Result:", res1)
|
||||
// note(zzy): multiple inputs with return value
|
||||
// only for test register & constraint,not have actual meaning
|
||||
// the ir compare cannot crossplatform currently
|
||||
// so just use a comment to test it
|
||||
res2 := asmFull("# calc {x} + {y} -> {}", map[string]any{
|
||||
"x": 25,
|
||||
"y": 17,
|
||||
})
|
||||
println("Result:", res2)
|
||||
}
|
||||
203
cl/_testrt/asmfull/out.ll
Normal file
203
cl/_testrt/asmfull/out.ll
Normal file
@@ -0,0 +1,203 @@
|
||||
; ModuleID = 'github.com/goplus/llgo/cl/_testrt/asmfull'
|
||||
source_filename = "github.com/goplus/llgo/cl/_testrt/asmfull"
|
||||
|
||||
%"github.com/goplus/llgo/runtime/internal/runtime.eface" = type { ptr, ptr }
|
||||
%"github.com/goplus/llgo/runtime/internal/runtime.String" = type { ptr, i64 }
|
||||
%"github.com/goplus/llgo/runtime/internal/runtime.Slice" = type { ptr, i64, i64 }
|
||||
%"github.com/goplus/llgo/runtime/abi.StructField" = type { %"github.com/goplus/llgo/runtime/internal/runtime.String", ptr, i64, %"github.com/goplus/llgo/runtime/internal/runtime.String", i1 }
|
||||
|
||||
@"github.com/goplus/llgo/cl/_testrt/asmfull.init$guard" = global i1 false, align 1
|
||||
@_llgo_string = linkonce global ptr null, align 8
|
||||
@_llgo_any = linkonce global ptr null, align 8
|
||||
@0 = private unnamed_addr constant [41 x i8] c"github.com/goplus/llgo/cl/_testrt/asmfull", align 1
|
||||
@"map[_llgo_string]_llgo_any" = linkonce global ptr null, align 8
|
||||
@1 = private unnamed_addr constant [7 x i8] c"topbits", align 1
|
||||
@2 = private unnamed_addr constant [4 x i8] c"keys", align 1
|
||||
@3 = private unnamed_addr constant [5 x i8] c"elems", align 1
|
||||
@4 = private unnamed_addr constant [8 x i8] c"overflow", align 1
|
||||
@_llgo_int = linkonce global ptr null, align 8
|
||||
@5 = private unnamed_addr constant [5 x i8] c"value", align 1
|
||||
@6 = private unnamed_addr constant [7 x i8] c"Result:", align 1
|
||||
@7 = private unnamed_addr constant [1 x i8] c"x", align 1
|
||||
@8 = private unnamed_addr constant [1 x i8] c"y", align 1
|
||||
|
||||
define void @"github.com/goplus/llgo/cl/_testrt/asmfull.init"() {
|
||||
_llgo_0:
|
||||
%0 = load i1, ptr @"github.com/goplus/llgo/cl/_testrt/asmfull.init$guard", align 1
|
||||
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
store i1 true, ptr @"github.com/goplus/llgo/cl/_testrt/asmfull.init$guard", align 1
|
||||
call void @"github.com/goplus/llgo/cl/_testrt/asmfull.init$after"()
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"github.com/goplus/llgo/cl/_testrt/asmfull.main"() {
|
||||
_llgo_0:
|
||||
call void asm sideeffect "nop", ""()
|
||||
%0 = load ptr, ptr @_llgo_string, align 8
|
||||
%1 = load ptr, ptr @_llgo_any, align 8
|
||||
%2 = load ptr, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
%3 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MakeMap"(ptr %2, i64 1)
|
||||
%4 = load ptr, ptr @_llgo_int, align 8
|
||||
%5 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %4, 0
|
||||
%6 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %5, ptr inttoptr (i64 42 to ptr), 1
|
||||
%7 = load ptr, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
%8 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16)
|
||||
store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @5, i64 5 }, ptr %8, align 8
|
||||
%9 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MapAssign"(ptr %7, ptr %3, ptr %8)
|
||||
store %"github.com/goplus/llgo/runtime/internal/runtime.eface" %6, ptr %9, align 8
|
||||
call void asm sideeffect "# test value ${0}", "r"(i64 42)
|
||||
%10 = load ptr, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
%11 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MakeMap"(ptr %10, i64 1)
|
||||
%12 = load ptr, ptr @_llgo_int, align 8
|
||||
%13 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %12, 0
|
||||
%14 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %13, ptr inttoptr (i64 42 to ptr), 1
|
||||
%15 = load ptr, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
%16 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16)
|
||||
store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @5, i64 5 }, ptr %16, align 8
|
||||
%17 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MapAssign"(ptr %15, ptr %11, ptr %16)
|
||||
store %"github.com/goplus/llgo/runtime/internal/runtime.eface" %14, ptr %17, align 8
|
||||
%18 = call i64 asm sideeffect "mov $0, ${1}", "=&r,r"(i64 42)
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintString"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @6, i64 7 })
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8 32)
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintUint"(i64 %18)
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8 10)
|
||||
%19 = load ptr, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
%20 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MakeMap"(ptr %19, i64 2)
|
||||
%21 = load ptr, ptr @_llgo_int, align 8
|
||||
%22 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %21, 0
|
||||
%23 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %22, ptr inttoptr (i64 25 to ptr), 1
|
||||
%24 = load ptr, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
%25 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16)
|
||||
store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @7, i64 1 }, ptr %25, align 8
|
||||
%26 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MapAssign"(ptr %24, ptr %20, ptr %25)
|
||||
store %"github.com/goplus/llgo/runtime/internal/runtime.eface" %23, ptr %26, align 8
|
||||
%27 = load ptr, ptr @_llgo_int, align 8
|
||||
%28 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" undef, ptr %27, 0
|
||||
%29 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.eface" %28, ptr inttoptr (i64 17 to ptr), 1
|
||||
%30 = load ptr, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
%31 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 16)
|
||||
store %"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @8, i64 1 }, ptr %31, align 8
|
||||
%32 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MapAssign"(ptr %30, ptr %20, ptr %31)
|
||||
store %"github.com/goplus/llgo/runtime/internal/runtime.eface" %29, ptr %32, align 8
|
||||
%33 = call i64 asm sideeffect "# calc ${1} + ${2} -> $0", "=&r,r,r"(i64 25, i64 17)
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintString"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @6, i64 7 })
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8 32)
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintUint"(i64 %33)
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8 10)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @"github.com/goplus/llgo/cl/_testrt/asmfull.init$after"() {
|
||||
_llgo_0:
|
||||
%0 = load ptr, ptr @_llgo_string, align 8
|
||||
%1 = icmp eq ptr %0, null
|
||||
br i1 %1, label %_llgo_1, label %_llgo_2
|
||||
|
||||
_llgo_1: ; preds = %_llgo_0
|
||||
%2 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 24)
|
||||
store ptr %2, ptr @_llgo_string, align 8
|
||||
br label %_llgo_2
|
||||
|
||||
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
%3 = load ptr, ptr @_llgo_any, align 8
|
||||
%4 = icmp eq ptr %3, null
|
||||
br i1 %4, label %_llgo_3, label %_llgo_4
|
||||
|
||||
_llgo_3: ; preds = %_llgo_2
|
||||
%5 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 0)
|
||||
%6 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %5, 0
|
||||
%7 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %6, i64 0, 1
|
||||
%8 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %7, i64 0, 2
|
||||
%9 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %8)
|
||||
store ptr %9, ptr @_llgo_any, align 8
|
||||
br label %_llgo_4
|
||||
|
||||
_llgo_4: ; preds = %_llgo_3, %_llgo_2
|
||||
%10 = load ptr, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
%11 = icmp eq ptr %10, null
|
||||
br i1 %11, label %_llgo_5, label %_llgo_6
|
||||
|
||||
_llgo_5: ; preds = %_llgo_4
|
||||
%12 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 24)
|
||||
%13 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 0)
|
||||
%14 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %13, 0
|
||||
%15 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %14, i64 0, 1
|
||||
%16 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %15, i64 0, 2
|
||||
%17 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %16)
|
||||
%18 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 40)
|
||||
%19 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64 8, ptr %18)
|
||||
%20 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @1, i64 7 }, ptr %19, i64 0, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
|
||||
%21 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 24)
|
||||
%22 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64 8, ptr %21)
|
||||
%23 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @2, i64 4 }, ptr %22, i64 8, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
|
||||
%24 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 0)
|
||||
%25 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %24, 0
|
||||
%26 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %25, i64 0, 1
|
||||
%27 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %26, i64 0, 2
|
||||
%28 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 41 }, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %27)
|
||||
%29 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64 8, ptr %28)
|
||||
%30 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @3, i64 5 }, ptr %29, i64 136, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
|
||||
%31 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 58)
|
||||
%32 = call %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @4, i64 8 }, ptr %31, i64 264, %"github.com/goplus/llgo/runtime/internal/runtime.String" zeroinitializer, i1 false)
|
||||
%33 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64 224)
|
||||
%34 = getelementptr %"github.com/goplus/llgo/runtime/abi.StructField", ptr %33, i64 0
|
||||
store %"github.com/goplus/llgo/runtime/abi.StructField" %20, ptr %34, align 8
|
||||
%35 = getelementptr %"github.com/goplus/llgo/runtime/abi.StructField", ptr %33, i64 1
|
||||
store %"github.com/goplus/llgo/runtime/abi.StructField" %23, ptr %35, align 8
|
||||
%36 = getelementptr %"github.com/goplus/llgo/runtime/abi.StructField", ptr %33, i64 2
|
||||
store %"github.com/goplus/llgo/runtime/abi.StructField" %30, ptr %36, align 8
|
||||
%37 = getelementptr %"github.com/goplus/llgo/runtime/abi.StructField", ptr %33, i64 3
|
||||
store %"github.com/goplus/llgo/runtime/abi.StructField" %32, ptr %37, align 8
|
||||
%38 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" undef, ptr %33, 0
|
||||
%39 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %38, i64 4, 1
|
||||
%40 = insertvalue %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %39, i64 4, 2
|
||||
%41 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Struct"(%"github.com/goplus/llgo/runtime/internal/runtime.String" { ptr @0, i64 41 }, i64 272, %"github.com/goplus/llgo/runtime/internal/runtime.Slice" %40)
|
||||
%42 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.MapOf"(ptr %12, ptr %17, ptr %41, i64 12)
|
||||
call void @"github.com/goplus/llgo/runtime/internal/runtime.SetDirectIface"(ptr %42)
|
||||
store ptr %42, ptr @"map[_llgo_string]_llgo_any", align 8
|
||||
br label %_llgo_6
|
||||
|
||||
_llgo_6: ; preds = %_llgo_5, %_llgo_4
|
||||
%43 = load ptr, ptr @_llgo_int, align 8
|
||||
%44 = icmp eq ptr %43, null
|
||||
br i1 %44, label %_llgo_7, label %_llgo_8
|
||||
|
||||
_llgo_7: ; preds = %_llgo_6
|
||||
%45 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64 34)
|
||||
store ptr %45, ptr @_llgo_int, align 8
|
||||
br label %_llgo_8
|
||||
|
||||
_llgo_8: ; preds = %_llgo_7, %_llgo_6
|
||||
ret void
|
||||
}
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.Basic"(i64)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.Interface"(%"github.com/goplus/llgo/runtime/internal/runtime.String", %"github.com/goplus/llgo/runtime/internal/runtime.Slice")
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.AllocU"(i64)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.MapOf"(ptr, ptr, ptr, i64)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.Struct"(%"github.com/goplus/llgo/runtime/internal/runtime.String", i64, %"github.com/goplus/llgo/runtime/internal/runtime.Slice")
|
||||
|
||||
declare %"github.com/goplus/llgo/runtime/abi.StructField" @"github.com/goplus/llgo/runtime/internal/runtime.StructField"(%"github.com/goplus/llgo/runtime/internal/runtime.String", ptr, i64, %"github.com/goplus/llgo/runtime/internal/runtime.String", i1)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.ArrayOf"(i64, ptr)
|
||||
|
||||
declare void @"github.com/goplus/llgo/runtime/internal/runtime.SetDirectIface"(ptr)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.MakeMap"(ptr, i64)
|
||||
|
||||
declare ptr @"github.com/goplus/llgo/runtime/internal/runtime.MapAssign"(ptr, ptr, ptr)
|
||||
|
||||
declare void @"github.com/goplus/llgo/runtime/internal/runtime.PrintString"(%"github.com/goplus/llgo/runtime/internal/runtime.String")
|
||||
|
||||
declare void @"github.com/goplus/llgo/runtime/internal/runtime.PrintByte"(i8)
|
||||
|
||||
declare void @"github.com/goplus/llgo/runtime/internal/runtime.PrintUint"(i64)
|
||||
@@ -270,6 +270,52 @@ func TestErrBuiltin(t *testing.T) {
|
||||
test("atomicCmpXchg", func(ctx *context) { ctx.atomicCmpXchg(nil, nil) })
|
||||
}
|
||||
|
||||
func TestErrAsm(t *testing.T) {
|
||||
test := func(testName string, fn func(ctx *context)) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Fatal(testName, ": no error?")
|
||||
}
|
||||
}()
|
||||
var ctx context
|
||||
fn(&ctx)
|
||||
}
|
||||
|
||||
test("asm(NoArgs)", func(ctx *context) { ctx.asm(nil, []ssa.Value{}) })
|
||||
test("asm(Nonconst)", func(ctx *context) { ctx.asm(nil, []ssa.Value{&ssa.Parameter{}}) })
|
||||
test("asmFull(Nonconst)", func(ctx *context) { ctx.asm(nil, []ssa.Value{&ssa.Parameter{}, &ssa.Parameter{}}) })
|
||||
test("asmFull(NonConstKey)", func(ctx *context) {
|
||||
makeMap := &ssa.MakeMap{}
|
||||
nonConstKey := &ssa.Parameter{}
|
||||
mapUpdate := &ssa.MapUpdate{Key: nonConstKey}
|
||||
referrers := []ssa.Instruction{mapUpdate}
|
||||
setRefs(unsafe.Pointer(makeMap), referrers...)
|
||||
strConst := &ssa.Const{
|
||||
Value: constant.MakeString("nop"),
|
||||
}
|
||||
ctx.asm(nil, []ssa.Value{strConst, makeMap})
|
||||
})
|
||||
test("asmFull(RegisterNotFound)", func(ctx *context) {
|
||||
makeMap := &ssa.MakeMap{}
|
||||
referrers := []ssa.Instruction{}
|
||||
setRefs(unsafe.Pointer(makeMap), referrers...)
|
||||
strConst := &ssa.Const{
|
||||
Value: constant.MakeString("test {missing}"),
|
||||
}
|
||||
ctx.asm(nil, []ssa.Value{strConst, makeMap})
|
||||
})
|
||||
test("asmFull(UnknownReferrer)", func(ctx *context) {
|
||||
makeMap := &ssa.MakeMap{}
|
||||
unknownRef := &ssa.Return{}
|
||||
referrers := []ssa.Instruction{unknownRef}
|
||||
setRefs(unsafe.Pointer(makeMap), referrers...)
|
||||
strConst := &ssa.Const{
|
||||
Value: constant.MakeString("test"),
|
||||
}
|
||||
ctx.asm(nil, []ssa.Value{strConst, makeMap})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPkgNoInit(t *testing.T) {
|
||||
pkg := types.NewPackage("foo", "foo")
|
||||
ctx := &context{
|
||||
|
||||
94
cl/instr.go
94
cl/instr.go
@@ -17,15 +17,20 @@
|
||||
package cl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/types"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ssa"
|
||||
|
||||
llssa "github.com/goplus/llgo/ssa"
|
||||
)
|
||||
|
||||
var asmRegisterRegex = regexp.MustCompile(`\{[a-zA-Z]+\}`)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
func constStr(v ssa.Value) (ret string, ok bool) {
|
||||
@@ -67,14 +72,93 @@ func cstr(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||
}
|
||||
|
||||
// func asm(string)
|
||||
func asm(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||
// func asm(string, map[string]any)
|
||||
func (p *context) asm(b llssa.Builder, args []ssa.Value) (ret llssa.Expr) {
|
||||
if len(args) == 0 || len(args) > 2 {
|
||||
panic("asm: invalid arguments - expected asm(<string-literal>) or asm(<string-literal>, <map-literal>)")
|
||||
}
|
||||
|
||||
asmString, ok := constStr(args[0])
|
||||
if !ok {
|
||||
panic("asm: inline assembly requires a constant string")
|
||||
}
|
||||
if len(args) == 1 {
|
||||
if sv, ok := constStr(args[0]); ok {
|
||||
b.InlineAsm(sv)
|
||||
b.InlineAsm(asmString)
|
||||
return llssa.Expr{Type: b.Prog.Void()}
|
||||
}
|
||||
|
||||
registers := make(map[string]llssa.Expr)
|
||||
if registerMap, ok := args[1].(*ssa.MakeMap); ok {
|
||||
referrers := registerMap.Referrers()
|
||||
for _, r := range *referrers {
|
||||
switch r := r.(type) {
|
||||
case *ssa.DebugRef, *ssa.Call:
|
||||
// ignore
|
||||
case *ssa.MapUpdate:
|
||||
if r.Block() != registerMap.Block() {
|
||||
panic("asm: register value map must be created in the same basic block")
|
||||
}
|
||||
panic("asm(<string-literal>): invalid arguments")
|
||||
key, ok := constStr(r.Key)
|
||||
if !ok {
|
||||
panic("asm: register key must be a string constant")
|
||||
}
|
||||
llvmValue := p.compileValue(b, r.Value.(*ssa.MakeInterface).X)
|
||||
registers[key] = llvmValue
|
||||
default:
|
||||
panic(fmt.Sprintf("asm: don't know how to handle argument to inline assembly: %s", r.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finalAsm := asmString
|
||||
var hasOutput bool
|
||||
var inputValues []llssa.Expr
|
||||
var constraints []string
|
||||
registerNumbers := map[string]int{}
|
||||
|
||||
if strings.Contains(finalAsm, "{}") {
|
||||
finalAsm = strings.ReplaceAll(finalAsm, "{}", "$0")
|
||||
constraints = append(constraints, "=&r")
|
||||
registerNumbers[""] = 0
|
||||
hasOutput = true
|
||||
}
|
||||
|
||||
finalAsm = asmRegisterRegex.ReplaceAllStringFunc(finalAsm, func(s string) string {
|
||||
// TODO: skip strings like {r4} etc. that look like ARM push/pop
|
||||
// instructions.
|
||||
name := s[1 : len(s)-1]
|
||||
value, ok := registers[name]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("asm: register not found: %s", name))
|
||||
}
|
||||
if _, ok := registerNumbers[name]; !ok {
|
||||
// Type checking - only allow integer basic types
|
||||
rawType := value.Type.RawType()
|
||||
if basic, ok := rawType.Underlying().(*types.Basic); ok && basic.Info()&types.IsInteger != 0 {
|
||||
registerNumbers[name] = len(registerNumbers)
|
||||
inputValues = append(inputValues, value)
|
||||
constraints = append(constraints, "r")
|
||||
} else {
|
||||
// Pointer operands support was dropped, following TinyGo
|
||||
// NOTE(tinygo): Memory references require a type starting with LLVM 14, probably as a preparation for opaque pointers.
|
||||
panic(fmt.Sprintf("asm: unsupported type in inline assembly for operand: %s, only integer types are supported", name))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("${%v}", registerNumbers[name])
|
||||
})
|
||||
|
||||
constraintStr := strings.Join(constraints, ",")
|
||||
if debugInstr {
|
||||
log.Printf("asm: %q -> %q, constraints: %q", asmString, finalAsm, constraintStr)
|
||||
}
|
||||
|
||||
if !hasOutput {
|
||||
// Make sure we return something valid
|
||||
b.InlineAsmFull(finalAsm, constraintStr, b.Prog.Void(), inputValues)
|
||||
return b.Prog.Val((uintptr(0)))
|
||||
}
|
||||
|
||||
return b.InlineAsmFull(finalAsm, constraintStr, b.Prog.Uintptr(), inputValues)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -470,7 +554,7 @@ func (p *context) call(b llssa.Builder, act llssa.DoAction, call *ssa.CallCommon
|
||||
case llgoCstr:
|
||||
ret = cstr(b, args)
|
||||
case llgoAsm:
|
||||
ret = asm(b, args)
|
||||
ret = p.asm(b, args)
|
||||
case llgoCgoCString:
|
||||
ret = p.cgoCString(b, args)
|
||||
case llgoCgoCBytes:
|
||||
|
||||
2
go.mod
2
go.mod
@@ -7,7 +7,7 @@ toolchain go1.24.1
|
||||
require (
|
||||
github.com/goplus/cobra v1.9.12 //gop:class
|
||||
github.com/goplus/gogen v1.19.1
|
||||
github.com/goplus/lib v0.2.0
|
||||
github.com/goplus/lib v0.3.0
|
||||
github.com/goplus/llgo/runtime v0.0.0-00010101000000-000000000000
|
||||
github.com/goplus/llvm v0.8.5
|
||||
github.com/goplus/mod v0.17.1
|
||||
|
||||
4
go.sum
4
go.sum
@@ -4,8 +4,8 @@ github.com/goplus/cobra v1.9.12 h1:0F9EdEbeGyITGz+mqoHoJ5KpUw97p1CkxV74IexHw5s=
|
||||
github.com/goplus/cobra v1.9.12/go.mod h1:p4LhfNJDKEpiGjGiNn0crUXL5dUPA5DX2ztYpEJR34E=
|
||||
github.com/goplus/gogen v1.19.1 h1:L7jz60azeowj8zUq48tozETriTPBLqHb0nDj6PheANc=
|
||||
github.com/goplus/gogen v1.19.1/go.mod h1:owX2e1EyU5WD+Nm6oH2m/GXjLdlBYcwkLO4wN8HHXZI=
|
||||
github.com/goplus/lib v0.2.0 h1:AjqkN1XK5H23wZMMlpaUYAMCDAdSBQ2NMFrLtSh7W4g=
|
||||
github.com/goplus/lib v0.2.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=
|
||||
github.com/goplus/lib v0.3.0 h1:y0ZGb5Q/RikW1oMMB4Di7XIZIpuzh/7mlrR8HNbxXCA=
|
||||
github.com/goplus/lib v0.3.0/go.mod h1:SgJv3oPqLLHCu0gcL46ejOP3x7/2ry2Jtxu7ta32kp0=
|
||||
github.com/goplus/llvm v0.8.5 h1:DUnFeYC3Rco622tBEKGg8xkigRAV2fh5ZIfBCt7gOSs=
|
||||
github.com/goplus/llvm v0.8.5/go.mod h1:PeVK8GgzxwAYCiMiUAJb5wJR6xbhj989tu9oulKLLT4=
|
||||
github.com/goplus/mod v0.17.1 h1:ITovxDcc5zbURV/Wrp3/SBsYLgC1KrxY6pq1zMM2V94=
|
||||
|
||||
@@ -742,16 +742,20 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
|
||||
}
|
||||
|
||||
if IsFullRpathEnabled() {
|
||||
exargs := make([]string, 0, ctx.nLibdir<<1)
|
||||
// Treat every link-time library search path, specified by the -L parameter, as a runtime search path as well.
|
||||
// This is to ensure the final executable can locate libraries with a relocatable install_name
|
||||
// (e.g., "@rpath/libfoo.dylib") at runtime.
|
||||
rpaths := make(map[string]none)
|
||||
for _, arg := range linkArgs {
|
||||
if strings.HasPrefix(arg, "-L") {
|
||||
exargs = append(exargs, "-rpath", arg[2:])
|
||||
path := arg[2:]
|
||||
if _, ok := rpaths[path]; ok {
|
||||
continue
|
||||
}
|
||||
rpaths[path] = none{}
|
||||
linkArgs = append(linkArgs, "-rpath", path)
|
||||
}
|
||||
}
|
||||
linkArgs = append(linkArgs, exargs...)
|
||||
}
|
||||
|
||||
err = linkObjFiles(ctx, orgApp, objFiles, linkArgs, verbose)
|
||||
@@ -836,15 +840,16 @@ func isWasmTarget(goos string) bool {
|
||||
return slices.Contains([]string{"wasi", "js", "wasip1"}, goos)
|
||||
}
|
||||
|
||||
func needStart(conf *Config) bool {
|
||||
if conf.Target == "" {
|
||||
return !isWasmTarget(conf.Goos)
|
||||
func needStart(ctx *context) bool {
|
||||
if ctx.buildConf.Target == "" {
|
||||
return !isWasmTarget(ctx.buildConf.Goos)
|
||||
}
|
||||
switch conf.Target {
|
||||
switch ctx.buildConf.Target {
|
||||
case "wasip2":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
// since newlib-esp32 provides _start, we don't need to provide a fake _start function
|
||||
return ctx.crossCompile.Libc != "newlib-esp32"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,10 +906,10 @@ define weak void @_start() {
|
||||
}
|
||||
`
|
||||
mainDefine := "define i32 @main(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr"
|
||||
if !needStart(ctx.buildConf) && isWasmTarget(ctx.buildConf.Goos) {
|
||||
if !needStart(ctx) && isWasmTarget(ctx.buildConf.Goos) {
|
||||
mainDefine = "define hidden noundef i32 @__main_argc_argv(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr"
|
||||
}
|
||||
if !needStart(ctx.buildConf) {
|
||||
if !needStart(ctx) {
|
||||
startDefine = ""
|
||||
}
|
||||
mainCode := fmt.Sprintf(`; ModuleID = 'main'
|
||||
|
||||
@@ -446,6 +446,12 @@ func TestFlagMergingScenarios(t *testing.T) {
|
||||
expectComp: []string{"-O3", "-fPIC", "-Wall", "-Wextra", "-std=c11"},
|
||||
expectLink: []string{"-O3", "-lm", "-lpthread", "-static"},
|
||||
},
|
||||
{
|
||||
// case from https://github.com/goplus/llgo/issues/1244
|
||||
name: "issue 1244",
|
||||
envCFlags: "-w -pipe -mmacosx-version-min=15 -isysroot/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk",
|
||||
expectComp: []string{"-w", "-pipe", "-mmacosx-version-min=15", "-isysroot/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk"},
|
||||
},
|
||||
}
|
||||
|
||||
// Save original environment
|
||||
|
||||
103
internal/crosscompile/compile/compile.go
Normal file
103
internal/crosscompile/compile/compile.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package compile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/internal/clang"
|
||||
)
|
||||
|
||||
type CompileOptions struct {
|
||||
CC string // Compiler to use
|
||||
Linker string
|
||||
CCFLAGS []string
|
||||
CFLAGS []string
|
||||
LDFLAGS []string
|
||||
}
|
||||
|
||||
type CompileGroup struct {
|
||||
OutputFileName string
|
||||
Files []string // List of source files to compile
|
||||
CFlags []string // C compiler flags
|
||||
CCFlags []string
|
||||
LDFlags []string // Linker flags
|
||||
}
|
||||
|
||||
func (g CompileGroup) IsCompiled(outputDir string) bool {
|
||||
archive := filepath.Join(outputDir, filepath.Base(g.OutputFileName))
|
||||
_, err := os.Stat(archive)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (g CompileGroup) Compile(
|
||||
outputDir string, options CompileOptions,
|
||||
) (err error) {
|
||||
if g.IsCompiled(outputDir) {
|
||||
return
|
||||
}
|
||||
tmpCompileDir, err := os.MkdirTemp("", "compile-group*")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpCompileDir)
|
||||
|
||||
compileLDFlags := append(slices.Clone(options.LDFLAGS), g.LDFlags...)
|
||||
compileCCFlags := append(slices.Clone(options.CCFLAGS), g.CCFlags...)
|
||||
compileCFFlags := append(slices.Clone(options.CFLAGS), g.CFlags...)
|
||||
|
||||
cfg := clang.NewConfig(options.CC, compileCCFlags, compileCFFlags, compileLDFlags, options.Linker)
|
||||
|
||||
var objFiles []string
|
||||
|
||||
compiler := clang.NewCompiler(cfg)
|
||||
|
||||
compiler.Verbose = true
|
||||
|
||||
archive := filepath.Join(outputDir, filepath.Base(g.OutputFileName))
|
||||
fmt.Fprintf(os.Stderr, "Start to compile group %s to %s...\n", g.OutputFileName, archive)
|
||||
|
||||
for _, file := range g.Files {
|
||||
var tempObjFile *os.File
|
||||
tempObjFile, err = os.CreateTemp(tmpCompileDir, fmt.Sprintf("%s*.o", strings.ReplaceAll(file, string(os.PathSeparator), "-")))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
lang := "c"
|
||||
if filepath.Ext(file) == ".S" {
|
||||
lang = "assembler-with-cpp"
|
||||
}
|
||||
err = compiler.Compile("-o", tempObjFile.Name(), "-x", lang, "-c", file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
objFiles = append(objFiles, tempObjFile.Name())
|
||||
}
|
||||
|
||||
args := []string{"rcs", archive}
|
||||
args = append(args, objFiles...)
|
||||
|
||||
ccDir := filepath.Dir(options.CC)
|
||||
llvmAr := filepath.Join(ccDir, "llvm-ar")
|
||||
|
||||
cmd := exec.Command(llvmAr, args...)
|
||||
// TODO(MeteorsLiu): support verbose
|
||||
// cmd.Stdout = os.Stdout
|
||||
// cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
return
|
||||
}
|
||||
|
||||
// CompileConfig represents compilation configuration
|
||||
type CompileConfig struct {
|
||||
Url string
|
||||
Name string // compile name (e.g., "picolibc", "musl", "glibc")
|
||||
Groups []CompileGroup
|
||||
ArchiveSrcDir string
|
||||
LibcCFlags []string
|
||||
}
|
||||
209
internal/crosscompile/compile/compile_test.go
Normal file
209
internal/crosscompile/compile/compile_test.go
Normal file
@@ -0,0 +1,209 @@
|
||||
//go:build !llgo
|
||||
|
||||
package compile
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/goplus/llgo/xtool/nm"
|
||||
)
|
||||
|
||||
func TestIsCompile(t *testing.T) {
|
||||
t.Run("IsCompile Not Exists", func(t *testing.T) {
|
||||
cfg := CompileConfig{
|
||||
Groups: []CompileGroup{
|
||||
{
|
||||
OutputFileName: "fakefile1.a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if cfg.Groups[0].IsCompiled(".") {
|
||||
t.Errorf("unexpected result: should false")
|
||||
}
|
||||
})
|
||||
t.Run("IsCompile Exists", func(t *testing.T) {
|
||||
tmpFile, err := os.CreateTemp("", "test*.a")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(tmpFile.Name())
|
||||
cfg := CompileConfig{
|
||||
Groups: []CompileGroup{
|
||||
{
|
||||
OutputFileName: tmpFile.Name(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !cfg.Groups[0].IsCompiled(filepath.Dir(tmpFile.Name())) {
|
||||
t.Errorf("unexpected result: should true")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompile(t *testing.T) {
|
||||
t.Run("Skip compile", func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "test-compile*")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
tmpFile, err := os.CreateTemp(tmpDir, "test*.a")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
group := CompileGroup{
|
||||
OutputFileName: tmpFile.Name(),
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected result: should nil: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TmpDir Fail", func(t *testing.T) {
|
||||
tmpDir := filepath.Join(t.TempDir(), "test-compile")
|
||||
os.RemoveAll(tmpDir)
|
||||
|
||||
err := os.Mkdir(tmpDir, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
os.Setenv("TMPDIR", tmpDir)
|
||||
defer os.Unsetenv("TMPDIR")
|
||||
|
||||
group := CompileGroup{
|
||||
OutputFileName: "nop.a",
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("unexpected result: should not nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Compile", func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "test-compile*")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
tmpFile, err := os.CreateTemp(tmpDir, "test*.c")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = tmpFile.Write([]byte(`#include <math.h>
|
||||
void Foo() {
|
||||
double x = 2.0;
|
||||
double y = sqrt(x);
|
||||
(void) y ;
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
group := CompileGroup{
|
||||
OutputFileName: "nop.a",
|
||||
Files: []string{tmpFile.Name()},
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
CCFLAGS: []string{"-nostdinc"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("unexpected result: should not nil")
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected result: should not nil")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(tmpDir, "nop.a")); os.IsNotExist(err) {
|
||||
t.Error("unexpected result: compiled nop.a not found")
|
||||
return
|
||||
}
|
||||
|
||||
items, err := nm.New("").List(filepath.Join(tmpDir, "nop.a"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
want := "Foo"
|
||||
found := false
|
||||
loop:
|
||||
for _, item := range items {
|
||||
for _, sym := range item.Symbols {
|
||||
if strings.Contains(sym.Name, want) {
|
||||
found = true
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("cannot find symbol Foo")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Compile Asm", func(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "test-compile*")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
tmpFile, err := os.CreateTemp(tmpDir, "test*.S")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
_, err = tmpFile.Write([]byte(`
|
||||
.text
|
||||
.globl _test
|
||||
`))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
group := CompileGroup{
|
||||
OutputFileName: "nop.a",
|
||||
Files: []string{tmpFile.Name()},
|
||||
}
|
||||
err = group.Compile(tmpDir, CompileOptions{
|
||||
CC: "clang",
|
||||
Linker: "lld",
|
||||
CCFLAGS: []string{"--target=x86_64-linux-gnu"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected result: should nil %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
630
internal/crosscompile/compile/libc/libc_test.go
Normal file
630
internal/crosscompile/compile/libc/libc_test.go
Normal file
@@ -0,0 +1,630 @@
|
||||
package libc
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetPicolibcConfig(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "test-target"
|
||||
|
||||
config := GetPicolibcConfig(baseDir, target)
|
||||
|
||||
if config.Name != "picolibc" {
|
||||
t.Errorf("Expected Name 'picolibc', got '%s'", config.Name)
|
||||
}
|
||||
if config.ArchiveSrcDir != "picolibc-main" {
|
||||
t.Errorf("Expected ArchiveSrcDir 'picolibc-main', got '%s'", config.ArchiveSrcDir)
|
||||
}
|
||||
|
||||
// Test LibcCFlags
|
||||
if len(config.LibcCFlags) != 2 {
|
||||
t.Errorf("Expected 2 LibcCFlags, got %d", len(config.LibcCFlags))
|
||||
} else {
|
||||
expected := "-I" + baseDir
|
||||
if config.LibcCFlags[0] != expected {
|
||||
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.LibcCFlags[0])
|
||||
}
|
||||
|
||||
expected = "-isystem" + filepath.Join(baseDir, "newlib", "libc", "include")
|
||||
if config.LibcCFlags[1] != expected {
|
||||
t.Errorf("Expected LibcCFlags[1] to be '%s', got '%s'", expected, config.LibcCFlags[1])
|
||||
}
|
||||
}
|
||||
|
||||
// Test Groups configuration
|
||||
if len(config.Groups) != 1 {
|
||||
t.Errorf("Expected 1 group, got %d", len(config.Groups))
|
||||
} else {
|
||||
group := config.Groups[0]
|
||||
|
||||
// Test output file name
|
||||
expectedOutput := "libc-" + target + ".a"
|
||||
if group.OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, group.OutputFileName)
|
||||
}
|
||||
|
||||
// Test files list
|
||||
if len(group.Files) == 0 {
|
||||
t.Error("Expected non-empty files list")
|
||||
} else {
|
||||
// Check a few sample files
|
||||
sampleFiles := []string{
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "bcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-malloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "printf.c"),
|
||||
}
|
||||
|
||||
for _, sample := range sampleFiles {
|
||||
found := false
|
||||
for _, file := range group.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group files", sample)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test CFlags
|
||||
expectedCFlags := []string{
|
||||
"-D_COMPILING_NEWLIB",
|
||||
"-D_HAVE_ALIAS_ATTRIBUTE",
|
||||
"-DTINY_STDIO",
|
||||
"-DPOSIX_IO",
|
||||
"-DFORMAT_DEFAULT_INTEGER",
|
||||
"-D_IEEE_LIBM",
|
||||
"-D__OBSOLETE_MATH_FLOAT=1",
|
||||
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||
"-D_WANT_IO_C99_FORMATS",
|
||||
"-nostdlib",
|
||||
"-I" + baseDir,
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libm", "common"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libc", "locale"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libc", "tinystdio"),
|
||||
}
|
||||
|
||||
if len(group.CFlags) != len(expectedCFlags) {
|
||||
t.Errorf("Expected %d CFlags, got %d", len(expectedCFlags), len(group.CFlags))
|
||||
} else {
|
||||
for i, expected := range expectedCFlags {
|
||||
if group.CFlags[i] != expected {
|
||||
t.Errorf("CFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, group.CFlags[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test LDFlags and CCFlags
|
||||
if len(group.LDFlags) == 0 {
|
||||
t.Error("Expected non-empty LDFlags")
|
||||
}
|
||||
if len(group.CCFlags) == 0 {
|
||||
t.Error("Expected non-empty CCFlags")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPicolibcConfig_EdgeCases(t *testing.T) {
|
||||
t.Run("EmptyBaseDir", func(t *testing.T) {
|
||||
config := GetPicolibcConfig("", "test-target")
|
||||
|
||||
// Check that paths are constructed correctly even with empty baseDir
|
||||
expected := "-I"
|
||||
if config.LibcCFlags[0] != expected {
|
||||
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.LibcCFlags[0])
|
||||
}
|
||||
|
||||
expected = "-isystem" + filepath.Join("", "newlib", "libc", "include")
|
||||
if config.LibcCFlags[1] != expected {
|
||||
t.Errorf("Expected LibcCFlags[1] to be '%s', got '%s'", expected, config.LibcCFlags[1])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EmptyTarget", func(t *testing.T) {
|
||||
config := GetPicolibcConfig("/test/base", "")
|
||||
|
||||
// Check output file name formatting
|
||||
expectedOutput := "libc-.a"
|
||||
if config.Groups[0].OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetNewlibESP32ConfigRISCV(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "riscv32-unknown-elf"
|
||||
|
||||
config := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
|
||||
// Test basic configuration
|
||||
if config.Url != _newlibUrl {
|
||||
t.Errorf("Expected URL '%s', got '%s'", _newlibUrl, config.Url)
|
||||
}
|
||||
if config.Name != "newlib-esp32" {
|
||||
t.Errorf("Expected Name 'newlib-esp32', got '%s'", config.Name)
|
||||
}
|
||||
if config.ArchiveSrcDir != _archiveInternalSrcDir {
|
||||
t.Errorf("Expected ArchiveSrcDir '%s', got '%s'", _archiveInternalSrcDir, config.ArchiveSrcDir)
|
||||
}
|
||||
|
||||
// Test LibcCFlags
|
||||
libcDir := filepath.Join(baseDir, "newlib", "libc")
|
||||
expectedCFlags := []string{
|
||||
"-isystem" + filepath.Join(libcDir, "include"),
|
||||
"-I" + filepath.Join(baseDir, "newlib"),
|
||||
"-I" + libcDir,
|
||||
}
|
||||
if len(config.LibcCFlags) != len(expectedCFlags) {
|
||||
t.Errorf("Expected %d LibcCFlags, got %d", len(expectedCFlags), len(config.LibcCFlags))
|
||||
} else {
|
||||
for i, expected := range expectedCFlags {
|
||||
if config.LibcCFlags[i] != expected {
|
||||
t.Errorf("LibcCFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, config.LibcCFlags[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test Groups configuration
|
||||
if len(config.Groups) != 3 {
|
||||
t.Errorf("Expected 3 groups, got %d", len(config.Groups))
|
||||
} else {
|
||||
// Group 0: libcrt0
|
||||
group0 := config.Groups[0]
|
||||
expectedOutput0 := "libcrt0-" + target + ".a"
|
||||
if group0.OutputFileName != expectedOutput0 {
|
||||
t.Errorf("Group0 OutputFileName expected '%s', got '%s'", expectedOutput0, group0.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group0
|
||||
sampleFiles0 := []string{
|
||||
filepath.Join(baseDir, "libgloss", "riscv", "esp", "esp_board.c"),
|
||||
filepath.Join(baseDir, "libgloss", "riscv", "esp", "crt1-board.S"),
|
||||
}
|
||||
for _, sample := range sampleFiles0 {
|
||||
found := false
|
||||
for _, file := range group0.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group0 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Group 1: libgloss
|
||||
group1 := config.Groups[1]
|
||||
expectedOutput1 := "libgloss-" + target + ".a"
|
||||
if group1.OutputFileName != expectedOutput1 {
|
||||
t.Errorf("Group1 OutputFileName expected '%s', got '%s'", expectedOutput1, group1.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group1
|
||||
sampleFiles1 := []string{
|
||||
filepath.Join(baseDir, "libgloss", "libnosys", "close.c"),
|
||||
filepath.Join(baseDir, "libgloss", "libnosys", "sbrk.c"),
|
||||
}
|
||||
for _, sample := range sampleFiles1 {
|
||||
found := false
|
||||
for _, file := range group1.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group1 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Group 2: libc
|
||||
group2 := config.Groups[2]
|
||||
expectedOutput2 := "libc-" + target + ".a"
|
||||
if group2.OutputFileName != expectedOutput2 {
|
||||
t.Errorf("Group2 OutputFileName expected '%s', got '%s'", expectedOutput2, group2.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group2
|
||||
sampleFiles2 := []string{
|
||||
filepath.Join(libcDir, "string", "memcpy.c"),
|
||||
filepath.Join(libcDir, "stdlib", "malloc.c"),
|
||||
}
|
||||
for _, sample := range sampleFiles2 {
|
||||
found := false
|
||||
for _, file := range group2.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group2 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Test CFlags for group2
|
||||
expectedCFlagsGroup2 := []string{
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-D_LIBC",
|
||||
"-DHAVE_NANOSLEEP",
|
||||
"-D__NO_SYSCALLS__",
|
||||
// ... (other expected flags)
|
||||
}
|
||||
for _, expectedFlag := range expectedCFlagsGroup2 {
|
||||
found := false
|
||||
for _, flag := range group2.CFlags {
|
||||
if flag == expectedFlag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected flag '%s' not found in group2 CFlags", expectedFlag)
|
||||
}
|
||||
}
|
||||
|
||||
// Test LDFlags and CCFlags
|
||||
if len(group0.LDFlags) == 0 {
|
||||
t.Error("Expected non-empty LDFlags in group0")
|
||||
}
|
||||
if len(group0.CCFlags) == 0 {
|
||||
t.Error("Expected non-empty CCFlags in group0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNewlibESP32ConfigXtensa(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "xtensa-esp32-elf"
|
||||
|
||||
config := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
|
||||
// Test basic configuration
|
||||
if config.Url != _newlibUrl {
|
||||
t.Errorf("Expected URL '%s', got '%s'", _newlibUrl, config.Url)
|
||||
}
|
||||
if config.Name != "newlib-esp32" {
|
||||
t.Errorf("Expected Name 'newlib-esp32', got '%s'", config.Name)
|
||||
}
|
||||
if config.ArchiveSrcDir != _archiveInternalSrcDir {
|
||||
t.Errorf("Expected ArchiveSrcDir '%s', got '%s'", _archiveInternalSrcDir, config.ArchiveSrcDir)
|
||||
}
|
||||
|
||||
// Test LibcCFlags
|
||||
libcDir := filepath.Join(baseDir, "newlib", "libc")
|
||||
expectedCFlags := []string{
|
||||
"-I" + filepath.Join(libcDir, "include"),
|
||||
"-I" + filepath.Join(baseDir, "newlib"),
|
||||
"-I" + libcDir,
|
||||
}
|
||||
if len(config.LibcCFlags) != len(expectedCFlags) {
|
||||
t.Errorf("Expected %d LibcCFlags, got %d", len(expectedCFlags), len(config.LibcCFlags))
|
||||
} else {
|
||||
for i, expected := range expectedCFlags {
|
||||
if config.LibcCFlags[i] != expected {
|
||||
t.Errorf("LibcCFlags[%d] mismatch. Expected '%s', got '%s'", i, expected, config.LibcCFlags[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test Groups configuration
|
||||
if len(config.Groups) != 3 {
|
||||
t.Errorf("Expected 2 groups, got %d", len(config.Groups))
|
||||
} else {
|
||||
// Group 0: libcrt0
|
||||
group0 := config.Groups[0]
|
||||
expectedOutput0 := "libcrt0-" + target + ".a"
|
||||
if group0.OutputFileName != expectedOutput0 {
|
||||
t.Errorf("Group0 OutputFileName expected '%s', got '%s'", expectedOutput0, group0.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group0
|
||||
sampleFiles0 := []string{
|
||||
filepath.Join(baseDir, "libgloss", "xtensa", "clibrary_init.c"),
|
||||
filepath.Join(baseDir, "libgloss", "xtensa", "crt1-boards.S"),
|
||||
}
|
||||
for _, sample := range sampleFiles0 {
|
||||
found := false
|
||||
for _, file := range group0.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group0 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Group 1: libgloss + libc
|
||||
group1 := config.Groups[1]
|
||||
expectedOutput1 := "libgloss-" + target + ".a"
|
||||
if group1.OutputFileName != expectedOutput1 {
|
||||
t.Errorf("Group1 OutputFileName expected '%s', got '%s'", expectedOutput1, group1.OutputFileName)
|
||||
}
|
||||
|
||||
// Check sample files in group1
|
||||
sampleFiles1 := []string{
|
||||
filepath.Join(baseDir, "libgloss", "libnosys", "close.c"),
|
||||
}
|
||||
for _, sample := range sampleFiles1 {
|
||||
found := false
|
||||
for _, file := range group1.Files {
|
||||
if file == sample {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected file '%s' not found in group1 files", sample)
|
||||
}
|
||||
}
|
||||
|
||||
// Test CFlags for group1
|
||||
expectedCFlagsGroup1 := []string{
|
||||
"-D__NO_SYSCALLS__",
|
||||
"-D_NO_GETUT",
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-D_LIBC",
|
||||
// ... (other expected flags)
|
||||
}
|
||||
for _, expectedFlag := range expectedCFlagsGroup1 {
|
||||
found := false
|
||||
for _, flag := range group1.CFlags {
|
||||
if flag == expectedFlag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected flag '%s' not found in group1 CFlags", expectedFlag)
|
||||
}
|
||||
}
|
||||
|
||||
// Test LDFlags and CCFlags
|
||||
if len(group0.LDFlags) == 0 {
|
||||
t.Error("Expected non-empty LDFlags in group0")
|
||||
}
|
||||
if len(group0.CCFlags) == 0 {
|
||||
t.Error("Expected non-empty CCFlags in group0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEdgeCases(t *testing.T) {
|
||||
t.Run("EmptyBaseDir_RISCV", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV("", "test-target")
|
||||
libcDir := filepath.Join("", "newlib", "libc")
|
||||
|
||||
// Check that paths are constructed correctly
|
||||
expected := "-isystem" + filepath.Join(libcDir, "include")
|
||||
if config.LibcCFlags[0] != expected {
|
||||
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.LibcCFlags[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EmptyTarget_RISCV", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV("/test/base", "")
|
||||
|
||||
// Check output file name formatting
|
||||
expectedOutput := "libcrt0-.a"
|
||||
if config.Groups[0].OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EmptyBaseDir_Xtensa", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa("", "test-target")
|
||||
libcDir := filepath.Join("", "newlib", "libc")
|
||||
|
||||
// Check that paths are constructed correctly
|
||||
expected := "-I" + filepath.Join(libcDir, "include")
|
||||
if config.LibcCFlags[0] != expected {
|
||||
t.Errorf("Expected LibcCFlags[0] to be '%s', got '%s'", expected, config.LibcCFlags[0])
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EmptyTarget_Xtensa", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa("/test/base", "")
|
||||
|
||||
// Check output file name formatting
|
||||
expectedOutput := "libcrt0-.a"
|
||||
if config.Groups[0].OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected OutputFileName '%s', got '%s'", expectedOutput, config.Groups[0].OutputFileName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGroupConfiguration(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "test-target"
|
||||
|
||||
t.Run("RISCV_GroupCount", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
if len(config.Groups) != 3 {
|
||||
t.Errorf("Expected 3 groups for RISCV, got %d", len(config.Groups))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Xtensa_GroupCount", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
if len(config.Groups) != 3 {
|
||||
t.Errorf("Expected 2 groups for Xtensa, got %d", len(config.Groups))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RISCV_GroupNames", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
expectedNames := []string{
|
||||
"libcrt0-" + target + ".a",
|
||||
"libgloss-" + target + ".a",
|
||||
"libc-" + target + ".a",
|
||||
}
|
||||
|
||||
for i, group := range config.Groups {
|
||||
if group.OutputFileName != expectedNames[i] {
|
||||
t.Errorf("Group %d expected name '%s', got '%s'", i, expectedNames[i], group.OutputFileName)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Xtensa_GroupNames", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
expectedNames := []string{
|
||||
"libcrt0-" + target + ".a",
|
||||
"libgloss-" + target + ".a",
|
||||
}
|
||||
|
||||
for i, group := range config.Groups {
|
||||
if i >= len(expectedNames) {
|
||||
return
|
||||
}
|
||||
if group.OutputFileName != expectedNames[i] {
|
||||
t.Errorf("Group %d expected name '%s', got '%s'", i, expectedNames[i], group.OutputFileName)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompilerFlags(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "test-target"
|
||||
|
||||
t.Run("RISCV_CFlags", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
group := config.Groups[2] // libc group
|
||||
|
||||
requiredFlags := []string{
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-D_LIBC",
|
||||
"-D__NO_SYSCALLS__",
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
}
|
||||
|
||||
for _, flag := range requiredFlags {
|
||||
found := false
|
||||
for _, cflag := range group.CFlags {
|
||||
if cflag == flag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required flag '%s' not found in RISCV CFlags", flag)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Xtensa_CFlags", func(t *testing.T) {
|
||||
config := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
group := config.Groups[1] // libgloss+libc group
|
||||
|
||||
requiredFlags := []string{
|
||||
"-D__NO_SYSCALLS__",
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-D_LIBC",
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
}
|
||||
|
||||
for _, flag := range requiredFlags {
|
||||
found := false
|
||||
for _, cflag := range group.CFlags {
|
||||
if cflag == flag {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required flag '%s' not found in Xtensa CFlags", flag)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CommonFlags", func(t *testing.T) {
|
||||
configRISCV := getNewlibESP32ConfigRISCV(baseDir, target)
|
||||
configXtensa := getNewlibESP32ConfigXtensa(baseDir, target)
|
||||
|
||||
// Test LDFlags
|
||||
expectedLDFlags := []string{
|
||||
"-nostdlib",
|
||||
"-ffunction-sections",
|
||||
"-fdata-sections",
|
||||
}
|
||||
|
||||
for _, group := range configRISCV.Groups {
|
||||
for _, expected := range expectedLDFlags {
|
||||
found := false
|
||||
for _, flag := range group.LDFlags {
|
||||
if flag == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required LDFlag '%s' not found in RISCV group", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, group := range configXtensa.Groups {
|
||||
for _, expected := range expectedLDFlags {
|
||||
found := false
|
||||
for _, flag := range group.LDFlags {
|
||||
if flag == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required LDFlag '%s' not found in Xtensa group", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test CCFlags
|
||||
expectedCCFlags := []string{
|
||||
"-Oz",
|
||||
"-fno-builtin",
|
||||
"-ffreestanding",
|
||||
}
|
||||
|
||||
for _, group := range configRISCV.Groups {
|
||||
for _, expected := range expectedCCFlags {
|
||||
found := false
|
||||
for _, flag := range group.CCFlags {
|
||||
if flag == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required CCFlag '%s' not found in RISCV group", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, group := range configXtensa.Groups {
|
||||
for _, expected := range expectedCCFlags {
|
||||
found := false
|
||||
for _, flag := range group.CCFlags {
|
||||
if flag == expected {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Required CCFlag '%s' not found in Xtensa group", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
2083
internal/crosscompile/compile/libc/newlibesp.go
Normal file
2083
internal/crosscompile/compile/libc/newlibesp.go
Normal file
File diff suppressed because it is too large
Load Diff
163
internal/crosscompile/compile/libc/picolibc.go
Normal file
163
internal/crosscompile/compile/libc/picolibc.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package libc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile"
|
||||
)
|
||||
|
||||
// getPicolibcConfig returns configuration for picolibc
|
||||
func GetPicolibcConfig(baseDir, target string) *compile.CompileConfig {
|
||||
return &compile.CompileConfig{
|
||||
Url: "https://github.com/goplus/picolibc/archive/refs/heads/main.zip",
|
||||
Name: "picolibc",
|
||||
LibcCFlags: []string{
|
||||
"-I" + baseDir,
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
},
|
||||
|
||||
Groups: []compile.CompileGroup{
|
||||
{
|
||||
OutputFileName: fmt.Sprintf("libc-%s.a", target),
|
||||
Files: []string{
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "bcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "bcopy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "bzero.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "explicit_bzero.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "ffsl.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "ffsll.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "fls.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "flsl.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "flsll.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "gnu_basename.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "index.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memccpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memmem.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memmove.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "mempcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memrchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "memset.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "rawmemchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "rindex.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "stpcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "stpncpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcasecmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcasecmp_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcasestr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strchrnul.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcoll.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcoll_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strcspn.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strerror_r.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlcat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strlwr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncasecmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncasecmp_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strncpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strndup.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strnlen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strnstr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strpbrk.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strrchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strsep.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strsignal.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strspn.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strstr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strtok.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strtok_r.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strupr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strverscmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strxfrm.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strxfrm_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "swab.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "timingsafe_bcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "timingsafe_memcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "strerror.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcpcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcpncpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscasecmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscasecmp_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcschr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscoll.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscoll_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcscspn.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsdup.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcslcat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcslcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcslen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncasecmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncasecmp_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncat.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsncpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsnlen.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcspbrk.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsrchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsspn.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsstr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcstok.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcswidth.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsxfrm.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcsxfrm_l.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wcwidth.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemchr.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemcmp.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemmove.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmempcpy.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "wmemset.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "string", "xpg_strerror_r.c"),
|
||||
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-calloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-malloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-pvalloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-realloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-valloc.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "rand.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "srand.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "stdlib", "nano-free.c"),
|
||||
|
||||
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "printf.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "putchar.c"),
|
||||
filepath.Join(baseDir, "newlib", "libc", "tinystdio", "puts.c"),
|
||||
},
|
||||
CFlags: []string{
|
||||
"-D_COMPILING_NEWLIB",
|
||||
"-D_HAVE_ALIAS_ATTRIBUTE",
|
||||
"-DTINY_STDIO",
|
||||
"-DPOSIX_IO",
|
||||
"-DFORMAT_DEFAULT_INTEGER",
|
||||
"-D_IEEE_LIBM",
|
||||
"-D__OBSOLETE_MATH_FLOAT=1",
|
||||
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||
"-D_WANT_IO_C99_FORMATS",
|
||||
"-nostdlib",
|
||||
"-I" + baseDir,
|
||||
"-isystem" + filepath.Join(baseDir, "newlib", "libc", "include"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libm", "common"),
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libc", "locale"),
|
||||
|
||||
"-I" + filepath.Join(baseDir, "newlib", "libc", "tinystdio"),
|
||||
},
|
||||
LDFlags: _libcLDFlags,
|
||||
CCFlags: _libcCCFlags,
|
||||
},
|
||||
},
|
||||
ArchiveSrcDir: "picolibc-main",
|
||||
}
|
||||
}
|
||||
288
internal/crosscompile/compile/rtlib/compiler_rt.go
Normal file
288
internal/crosscompile/compile/rtlib/compiler_rt.go
Normal file
@@ -0,0 +1,288 @@
|
||||
package rtlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile"
|
||||
)
|
||||
|
||||
func platformSpecifiedFiles(builtinsDir, target string) []string {
|
||||
switch {
|
||||
case strings.Contains(target, "riscv32"):
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "riscv", "mulsi3.S"),
|
||||
filepath.Join(builtinsDir, "riscv", "fp_mode.c"),
|
||||
filepath.Join(builtinsDir, "riscv", "save.S"),
|
||||
filepath.Join(builtinsDir, "riscv", "restore.S"),
|
||||
filepath.Join(builtinsDir, "atomic.c"),
|
||||
}
|
||||
case strings.Contains(target, "riscv64"):
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "addtf3.c"),
|
||||
filepath.Join(builtinsDir, "comparetf2.c"),
|
||||
filepath.Join(builtinsDir, "divtc3.c"),
|
||||
filepath.Join(builtinsDir, "divtf3.c"),
|
||||
filepath.Join(builtinsDir, "extenddftf2.c"),
|
||||
filepath.Join(builtinsDir, "extendhftf2.c"),
|
||||
filepath.Join(builtinsDir, "extendsftf2.c"),
|
||||
filepath.Join(builtinsDir, "fixtfdi.c"),
|
||||
filepath.Join(builtinsDir, "fixtfsi.c"),
|
||||
filepath.Join(builtinsDir, "fixtfti.c"),
|
||||
filepath.Join(builtinsDir, "fixunstfdi.c"),
|
||||
filepath.Join(builtinsDir, "fixunstfsi.c"),
|
||||
filepath.Join(builtinsDir, "fixunstfti.c"),
|
||||
filepath.Join(builtinsDir, "floatditf.c"),
|
||||
filepath.Join(builtinsDir, "floatsitf.c"),
|
||||
filepath.Join(builtinsDir, "floattitf.c"),
|
||||
filepath.Join(builtinsDir, "floatunditf.c"),
|
||||
filepath.Join(builtinsDir, "floatunsitf.c"),
|
||||
filepath.Join(builtinsDir, "floatuntitf.c"),
|
||||
filepath.Join(builtinsDir, "multc3.c"),
|
||||
filepath.Join(builtinsDir, "multf3.c"),
|
||||
filepath.Join(builtinsDir, "powitf2.c"),
|
||||
filepath.Join(builtinsDir, "subtf3.c"),
|
||||
filepath.Join(builtinsDir, "trunctfdf2.c"),
|
||||
filepath.Join(builtinsDir, "trunctfhf2.c"),
|
||||
filepath.Join(builtinsDir, "trunctfsf2.c"),
|
||||
filepath.Join(builtinsDir, "atomic.c"),
|
||||
}
|
||||
case strings.Contains(target, "arm"):
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_cdcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_cdcmpeq_check_nan.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_cfcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_cfcmpeq_check_nan.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_dcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_div0.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_drsub.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_fcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_frsub.c"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_idivmod.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_ldivmod.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_memcmp.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_memcpy.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_memmove.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_memset.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_uidivmod.S"),
|
||||
filepath.Join(builtinsDir, "arm", "aeabi_uldivmod.S"),
|
||||
|
||||
// These two are not technically EABI builtins but are used by them and only
|
||||
// seem to be used on ARM. LLVM seems to use __divsi3 and __modsi3 on most
|
||||
// other architectures.
|
||||
// Most importantly, they have a different calling convention on AVR so
|
||||
// should not be used on AVR.
|
||||
filepath.Join(builtinsDir, "divmodsi4.c"),
|
||||
filepath.Join(builtinsDir, "udivmodsi4.c"),
|
||||
}
|
||||
case strings.Contains(target, "avr"):
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "avr", "divmodhi4.S"),
|
||||
filepath.Join(builtinsDir, "avr", "divmodqi4.S"),
|
||||
filepath.Join(builtinsDir, "avr", "mulhi3.S"),
|
||||
filepath.Join(builtinsDir, "avr", "mulqi3.S"),
|
||||
filepath.Join(builtinsDir, "avr", "udivmodhi4.S"),
|
||||
filepath.Join(builtinsDir, "avr", "udivmodqi4.S"),
|
||||
}
|
||||
|
||||
case target == "xtensa":
|
||||
return []string{
|
||||
filepath.Join(builtinsDir, "xtensa", "ieee754_sqrtf.S"),
|
||||
filepath.Join(builtinsDir, "atomic.c"),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func withPlatformSpecifiedFiles(baseDir, target string, files []string) []string {
|
||||
builtinsDir := filepath.Join(baseDir, "lib", "builtins")
|
||||
return append(files, platformSpecifiedFiles(builtinsDir, target)...)
|
||||
}
|
||||
|
||||
func GetCompilerRTConfig(baseDir, target string) *compile.CompileConfig {
|
||||
return &compile.CompileConfig{
|
||||
Url: "https://github.com/goplus/compiler-rt/archive/refs/tags/v0.1.0.tar.gz",
|
||||
ArchiveSrcDir: "compiler-rt-0.1.0",
|
||||
Groups: []compile.CompileGroup{
|
||||
{
|
||||
OutputFileName: fmt.Sprintf("libclang_builtins-%s.a", target),
|
||||
Files: withPlatformSpecifiedFiles(baseDir, target, []string{
|
||||
filepath.Join(baseDir, "lib", "builtins", "absvdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "absvsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "absvti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "adddf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addsf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addvdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addvsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addvti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "apple_versioning.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ashldi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ashlti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ashrdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ashrti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "bswapdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "bswapsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "clzdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "clzsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "clzti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "cmpdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "cmpti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "comparedf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "comparesf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ctzdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ctzsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ctzti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divdc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divdf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divmoddi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divmodsi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divmodti4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divsc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divsf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extendsfdf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extendhfsf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ffsdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ffssi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ffsti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixdfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixdfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixdfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixsfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixsfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixsfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunsdfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunsdfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunsdfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunssfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunssfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunssfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatdidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatdisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatsidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatsisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floattidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floattisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatundidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatundisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatunsidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatunsisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatuntidf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatuntisf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fp_mode.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "int_util.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "lshrdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "lshrti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "moddi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "modsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "modti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "muldc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "muldf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "muldi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulodi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulosi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "muloti4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulsc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulsf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "multi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulvdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulvsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "mulvti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negdf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negsf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negvdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negvsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "negvti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "os_version_check.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "paritydi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "paritysi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "parityti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "popcountdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "popcountsi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "popcountti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "powidf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "powisf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subdf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subsf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subvdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subvsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subvti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "trampoline_setup.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "truncdfhf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "truncdfsf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "truncsfhf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ucmpdi2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "ucmpti2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivdi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivmoddi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivmodsi4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivmodti4.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "udivti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "umoddi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "umodsi3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "umodti3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "gcc_personality_v0.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "clear_cache.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "addtf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "comparetf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divtc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "divtf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extenddftf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extendhftf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "extendsftf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixtfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixtfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixtfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunstfdi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunstfsi.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "fixunstfti.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatditf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatsitf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floattitf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatunditf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatunsitf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "floatuntitf.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "multc3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "multf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "powitf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "subtf3.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "trunctfdf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "trunctfhf2.c"),
|
||||
filepath.Join(baseDir, "lib", "builtins", "trunctfsf2.c"),
|
||||
}),
|
||||
CFlags: []string{
|
||||
"-DNDEBUG",
|
||||
"-DVISIBILITY_HIDDEN",
|
||||
},
|
||||
CCFlags: []string{
|
||||
"-Oz",
|
||||
"-fno-ident",
|
||||
"-Wno-unused-parameter",
|
||||
"-fno-lto",
|
||||
"-Werror=array-bounds",
|
||||
"-Werror=uninitialized",
|
||||
"-Werror=shadow",
|
||||
"-Werror=empty-body",
|
||||
"-Werror=sizeof-pointer-memaccess",
|
||||
"-Werror=sizeof-array-argument",
|
||||
"-Werror=suspicious-memaccess",
|
||||
"-Werror=builtin-memcpy-chk-size",
|
||||
"-Werror=array-bounds-pointer-arithmetic",
|
||||
"-Werror=return-stack-address",
|
||||
"-Werror=sizeof-array-decay",
|
||||
"-Werror=format-insufficient-args",
|
||||
"-Wformat -std=c11",
|
||||
"-fno-builtin",
|
||||
"-fvisibility=hidden",
|
||||
"-fomit-frame-pointer",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
124
internal/crosscompile/compile/rtlib/rt_test.go
Normal file
124
internal/crosscompile/compile/rtlib/rt_test.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package rtlib
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPlatformSpecifiedFiles(t *testing.T) {
|
||||
tests := []struct {
|
||||
target string
|
||||
expected int // Number of expected files
|
||||
}{
|
||||
{"riscv32-unknown-elf", 5},
|
||||
{"riscv64-unknown-elf", 27},
|
||||
{"arm-none-eabi", 19},
|
||||
{"avr-unknown-elf", 6},
|
||||
{"xtensa", 2},
|
||||
{"x86_64-pc-windows", 0},
|
||||
}
|
||||
|
||||
builtinsDir := "/test/builtins"
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.target, func(t *testing.T) {
|
||||
result := platformSpecifiedFiles(builtinsDir, tt.target)
|
||||
if len(result) != tt.expected {
|
||||
t.Errorf("For target %s, expected %d files, got %d", tt.target, tt.expected, len(result))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPlatformSpecifiedFiles(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "riscv32-unknown-elf"
|
||||
inputFiles := []string{"file1.c", "file2.c"}
|
||||
|
||||
result := withPlatformSpecifiedFiles(baseDir, target, inputFiles)
|
||||
|
||||
// Should have input files + platform specific files
|
||||
if len(result) <= len(inputFiles) {
|
||||
t.Errorf("Expected more files than input, got %d", len(result))
|
||||
}
|
||||
|
||||
// Check that input files are preserved
|
||||
for _, inputFile := range inputFiles {
|
||||
found := false
|
||||
for _, resultFile := range result {
|
||||
if resultFile == inputFile {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Input file %s not found in result", inputFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCompilerRTConfig(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "riscv32-unknown-elf"
|
||||
|
||||
config := GetCompilerRTConfig(baseDir, target)
|
||||
|
||||
// Test groups configuration
|
||||
if len(config.Groups) != 1 {
|
||||
t.Errorf("Expected 1 group, got %d", len(config.Groups))
|
||||
} else {
|
||||
group := config.Groups[0]
|
||||
expectedOutput := "libclang_builtins-" + target + ".a"
|
||||
if group.OutputFileName != expectedOutput {
|
||||
t.Errorf("Expected output file %s, got %s", expectedOutput, group.OutputFileName)
|
||||
}
|
||||
|
||||
// Check that files list contains platform-specific files
|
||||
if len(group.Files) == 0 {
|
||||
t.Error("Expected non-empty files list")
|
||||
}
|
||||
|
||||
// Check that CFlags are set
|
||||
if len(group.CFlags) == 0 {
|
||||
t.Error("Expected non-empty CFlags")
|
||||
}
|
||||
|
||||
// Check that CCFlags are set
|
||||
if len(group.CCFlags) == 0 {
|
||||
t.Error("Expected non-empty CCFlags")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCompilerRTConfig_DifferentTargets(t *testing.T) {
|
||||
targets := []string{
|
||||
"riscv32-unknown-elf",
|
||||
"riscv64-unknown-elf",
|
||||
"arm-none-eabi",
|
||||
"avr-unknown-elf",
|
||||
"xtensa",
|
||||
}
|
||||
|
||||
baseDir := "/test/base"
|
||||
for _, target := range targets {
|
||||
t.Run(target, func(t *testing.T) {
|
||||
config := GetCompilerRTConfig(baseDir, target)
|
||||
|
||||
// Basic validation
|
||||
if config.Url == "" {
|
||||
t.Error("URL should not be empty")
|
||||
}
|
||||
if config.ArchiveSrcDir == "" {
|
||||
t.Error("ArchiveSrcDir should not be empty")
|
||||
}
|
||||
if len(config.Groups) == 0 {
|
||||
t.Error("Should have at least one group")
|
||||
}
|
||||
|
||||
// Check output filename contains target
|
||||
group := config.Groups[0]
|
||||
if !strings.Contains(group.OutputFileName, target) {
|
||||
t.Errorf("Output filename %s should contain target %s", group.OutputFileName, target)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile"
|
||||
"github.com/goplus/llgo/internal/env"
|
||||
"github.com/goplus/llgo/internal/targets"
|
||||
"github.com/goplus/llgo/internal/xtool/llvm"
|
||||
@@ -25,6 +26,7 @@ type Export struct {
|
||||
BuildTags []string
|
||||
GOOS string
|
||||
GOARCH string
|
||||
Libc string
|
||||
Linker string // Linker to use (e.g., "ld.lld", "avr-ld")
|
||||
ExtraFiles []string // Extra files to compile and link (e.g., .s, .c files)
|
||||
ClangRoot string // Root directory of custom clang installation
|
||||
@@ -219,6 +221,35 @@ func getESPClangPlatform(goos, goarch string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func ldFlagsFromFileName(fileName string) string {
|
||||
return strings.TrimPrefix(strings.TrimSuffix(fileName, ".a"), "lib")
|
||||
}
|
||||
|
||||
func getOrCompileWithConfig(
|
||||
compileConfig *compile.CompileConfig,
|
||||
outputDir string, options compile.CompileOptions,
|
||||
) (ldflags []string, err error) {
|
||||
if err = checkDownloadAndExtractLib(
|
||||
compileConfig.Url, outputDir,
|
||||
compileConfig.ArchiveSrcDir,
|
||||
); err != nil {
|
||||
return
|
||||
}
|
||||
ldflags = append(ldflags, "-nostdlib", "-L"+outputDir)
|
||||
|
||||
for _, group := range compileConfig.Groups {
|
||||
err = group.Compile(outputDir, options)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if filepath.Ext(group.OutputFileName) == ".o" {
|
||||
continue
|
||||
}
|
||||
ldflags = append(ldflags, "-l"+ldFlagsFromFileName(group.OutputFileName))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func use(goos, goarch string, wasiThreads, forceEspClang bool) (export Export, err error) {
|
||||
targetTriple := llvm.GetTargetTriple(goos, goarch)
|
||||
llgoRoot := env.LLGoROOT()
|
||||
@@ -583,6 +614,57 @@ func useTarget(targetName string) (export Export, err error) {
|
||||
}
|
||||
ldflags = append(ldflags, "-L", env.LLGoROOT()) // search targets/*.ld
|
||||
|
||||
var libcIncludeDir []string
|
||||
|
||||
if config.Libc != "" {
|
||||
var libcLDFlags []string
|
||||
var compileConfig *compile.CompileConfig
|
||||
baseDir := filepath.Join(cacheRoot(), "crosscompile")
|
||||
outputDir := filepath.Join(baseDir, config.Libc)
|
||||
|
||||
compileConfig, err = getLibcCompileConfigByName(baseDir, config.Libc, config.LLVMTarget, config.CPU)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
libcLDFlags, err = getOrCompileWithConfig(compileConfig, outputDir, compile.CompileOptions{
|
||||
CC: export.CC,
|
||||
Linker: export.Linker,
|
||||
CCFLAGS: ccflags,
|
||||
LDFLAGS: ldflags,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cflags = append(cflags, compileConfig.LibcCFlags...)
|
||||
ldflags = append(ldflags, libcLDFlags...)
|
||||
|
||||
libcIncludeDir = compileConfig.LibcCFlags
|
||||
export.Libc = config.Libc
|
||||
}
|
||||
|
||||
if config.RTLib != "" {
|
||||
var rtLibLDFlags []string
|
||||
var compileConfig *compile.CompileConfig
|
||||
baseDir := filepath.Join(cacheRoot(), "crosscompile")
|
||||
outputDir := filepath.Join(baseDir, config.RTLib)
|
||||
|
||||
compileConfig, err = getRTCompileConfigByName(baseDir, config.RTLib, config.LLVMTarget)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rtLibLDFlags, err = getOrCompileWithConfig(compileConfig, outputDir, compile.CompileOptions{
|
||||
CC: export.CC,
|
||||
Linker: export.Linker,
|
||||
CCFLAGS: ccflags,
|
||||
LDFLAGS: ldflags,
|
||||
CFLAGS: libcIncludeDir,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ldflags = append(ldflags, rtLibLDFlags...)
|
||||
}
|
||||
|
||||
// Combine with config flags and expand template variables
|
||||
export.CFLAGS = cflags
|
||||
export.CCFLAGS = ccflags
|
||||
@@ -595,7 +677,7 @@ func useTarget(targetName string) (export Export, err error) {
|
||||
// Use extends the original Use function to support target-based configuration
|
||||
// If targetName is provided, it takes precedence over goos/goarch
|
||||
func Use(goos, goarch, targetName string, wasiThreads, forceEspClang bool) (export Export, err error) {
|
||||
if targetName != "" {
|
||||
if targetName != "" && !strings.HasPrefix(targetName, "wasm") && !strings.HasPrefix(targetName, "wasi") {
|
||||
return useTarget(targetName)
|
||||
}
|
||||
return use(goos, goarch, wasiThreads, forceEspClang)
|
||||
|
||||
@@ -176,13 +176,14 @@ func TestUseTarget(t *testing.T) {
|
||||
expectLLVM string
|
||||
expectCPU string
|
||||
}{
|
||||
{
|
||||
name: "WASI Target",
|
||||
targetName: "wasi",
|
||||
expectError: false,
|
||||
expectLLVM: "",
|
||||
expectCPU: "generic",
|
||||
},
|
||||
// FIXME(MeteorsLiu): wasi in useTarget
|
||||
// {
|
||||
// name: "WASI Target",
|
||||
// targetName: "wasi",
|
||||
// expectError: false,
|
||||
// expectLLVM: "",
|
||||
// expectCPU: "generic",
|
||||
// },
|
||||
{
|
||||
name: "RP2040 Target",
|
||||
targetName: "rp2040",
|
||||
@@ -279,13 +280,13 @@ func TestUseTarget(t *testing.T) {
|
||||
|
||||
func TestUseWithTarget(t *testing.T) {
|
||||
// Test target-based configuration takes precedence
|
||||
export, err := Use("linux", "amd64", "wasi", false, true)
|
||||
export, err := Use("linux", "amd64", "esp32", false, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Check if LLVM target is in CCFLAGS
|
||||
found := slices.Contains(export.CCFLAGS, "-mcpu=generic")
|
||||
found := slices.Contains(export.CCFLAGS, "-mcpu=esp32")
|
||||
if !found {
|
||||
t.Errorf("Expected CPU generic in CCFLAGS, got %v", export.CCFLAGS)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ package crosscompile
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -78,6 +80,48 @@ func checkDownloadAndExtractESPClang(platformSuffix, dir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDownloadAndExtractLib(url, dstDir, internalArchiveSrcDir string) error {
|
||||
// Check if already exists
|
||||
if _, err := os.Stat(dstDir); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create lock file path for the final destination
|
||||
lockPath := dstDir + ".lock"
|
||||
lockFile, err := acquireLock(lockPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to acquire lock: %w", err)
|
||||
}
|
||||
defer releaseLock(lockFile)
|
||||
|
||||
// Double-check after acquiring lock
|
||||
if _, err := os.Stat(dstDir); err == nil {
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s not found in LLGO_ROOT or cache, will download and compile.\n", dstDir)
|
||||
|
||||
description := fmt.Sprintf("lib %s", path.Base(url))
|
||||
|
||||
// Use temporary extraction directory
|
||||
tempExtractDir := dstDir + ".extract"
|
||||
if err := downloadAndExtractArchive(url, tempExtractDir, description); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tempExtractDir)
|
||||
|
||||
srcDir := tempExtractDir
|
||||
|
||||
if internalArchiveSrcDir != "" {
|
||||
srcDir = filepath.Join(tempExtractDir, internalArchiveSrcDir)
|
||||
}
|
||||
|
||||
if err := os.Rename(srcDir, dstDir); err != nil {
|
||||
return fmt.Errorf("failed to rename lib directory: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// acquireLock creates and locks a file to prevent concurrent operations
|
||||
func acquireLock(lockPath string) (*os.File, error) {
|
||||
// Ensure the parent directory exists
|
||||
@@ -140,10 +184,14 @@ func downloadAndExtractArchive(url, destDir, description string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract %s archive: %w", description, err)
|
||||
}
|
||||
} else if strings.HasSuffix(filename, ".zip") {
|
||||
err := extractZip(localFile, tempDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract %s archive: %w", description, err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unsupported archive format: %s", filename)
|
||||
}
|
||||
|
||||
// Rename temp directory to target directory
|
||||
if err := os.Rename(tempDir, destDir); err != nil {
|
||||
return fmt.Errorf("failed to rename directory: %w", err)
|
||||
@@ -223,3 +271,44 @@ func extractTarXz(tarXzFile, dest string) error {
|
||||
cmd := exec.Command("tar", "-xf", tarXzFile, "-C", dest)
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func extractZip(zipFile, dest string) error {
|
||||
r, err := zip.OpenReader(zipFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
decompress := func(file *zip.File) error {
|
||||
path := filepath.Join(dest, file.Name)
|
||||
|
||||
if file.FileInfo().IsDir() {
|
||||
return os.MkdirAll(path, 0700)
|
||||
}
|
||||
|
||||
fs, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fs.Close()
|
||||
|
||||
w, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(w, fs); err != nil {
|
||||
w.Close()
|
||||
return err
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, file := range r.File {
|
||||
if err = decompress(file); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,10 +5,13 @@ package crosscompile
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -287,14 +290,14 @@ func TestDownloadAndExtractArchive(t *testing.T) {
|
||||
|
||||
func TestDownloadAndExtractArchiveUnsupportedFormat(t *testing.T) {
|
||||
server := createTestServer(t, map[string]string{
|
||||
"test.zip": "fake zip content",
|
||||
"test.7z": "fake zip content",
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
tempDir := t.TempDir()
|
||||
destDir := filepath.Join(tempDir, "extracted")
|
||||
|
||||
err := downloadAndExtractArchive(server.URL+"/test.zip", destDir, "Test Archive")
|
||||
err := downloadAndExtractArchive(server.URL+"/test.7z", destDir, "Test Archive")
|
||||
if err == nil {
|
||||
t.Error("Expected error for unsupported format, got nil")
|
||||
}
|
||||
@@ -303,6 +306,162 @@ func TestDownloadAndExtractArchiveUnsupportedFormat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckDownloadAndExtractLib(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"lib-src/file1.c": "int func1() { return 1; }",
|
||||
"lib-src/file2.c": "int func2() { return 2; }",
|
||||
"lib-src/include/lib.h": "#define LIB_VERSION 1",
|
||||
}
|
||||
|
||||
archivePath := createTestTarGz(t, files)
|
||||
defer os.Remove(archivePath)
|
||||
|
||||
archiveContent, err := os.ReadFile(archivePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read test archive: %v", err)
|
||||
}
|
||||
|
||||
server := createTestServer(t, map[string]string{
|
||||
"test-lib.tar.gz": string(archiveContent),
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
tempDir := t.TempDir()
|
||||
destDir := filepath.Join(tempDir, "test-lib")
|
||||
|
||||
t.Run("LibAlreadyExists", func(t *testing.T) {
|
||||
if err := os.MkdirAll(destDir, 0755); err != nil {
|
||||
t.Fatalf("Failed to create existing lib dir: %v", err)
|
||||
}
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/test-lib.tar.gz", destDir, "")
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error when lib exists, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DownloadAndExtractWithoutInternalDir", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/test-lib.tar.gz", destDir, "lib-src")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to download and extract lib: %v", err)
|
||||
}
|
||||
cmd := exec.Command("ls", destDir)
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Run()
|
||||
|
||||
for name, expectedContent := range files {
|
||||
relPath := strings.TrimPrefix(name, "lib-src/")
|
||||
filePath := filepath.Join(destDir, relPath)
|
||||
|
||||
fmt.Println(filePath, destDir)
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read extracted file %s: %v", relPath, err)
|
||||
continue
|
||||
}
|
||||
if string(content) != expectedContent {
|
||||
t.Errorf("File %s: expected content %q, got %q", relPath, expectedContent, string(content))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DownloadAndExtractWithInternalDir", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/test-lib.tar.gz", destDir, "lib-src")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to download and extract lib: %v", err)
|
||||
}
|
||||
|
||||
for name, expectedContent := range files {
|
||||
relPath := strings.TrimPrefix(name, "lib-src/")
|
||||
filePath := filepath.Join(destDir, relPath)
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read extracted file %s: %v", relPath, err)
|
||||
continue
|
||||
}
|
||||
if string(content) != expectedContent {
|
||||
t.Errorf("File %s: expected content %q, got %q", relPath, expectedContent, string(content))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DownloadFailure", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/nonexistent.tar.gz", destDir, "")
|
||||
if err == nil {
|
||||
t.Error("Expected error for non-existent archive, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RenameFailure", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/test-lib.tar.gz", destDir, "lib-src222")
|
||||
if err == nil {
|
||||
t.Error("Expected error for rename failure, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckDownloadAndExtractLibInternalDir(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"project-1.0.0/src/file1.c": "int func1() { return 1; }",
|
||||
"project-1.0.0/include/lib.h": "#define LIB_VERSION 1",
|
||||
"project-1.0.0/README.md": "Project documentation",
|
||||
}
|
||||
|
||||
archivePath := createTestTarGz(t, files)
|
||||
defer os.Remove(archivePath)
|
||||
|
||||
archiveContent, err := os.ReadFile(archivePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read test archive: %v", err)
|
||||
}
|
||||
|
||||
server := createTestServer(t, map[string]string{
|
||||
"project.tar.gz": string(archiveContent),
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
tempDir := t.TempDir()
|
||||
destDir := filepath.Join(tempDir, "project-lib")
|
||||
|
||||
t.Run("CorrectInternalDir", func(t *testing.T) {
|
||||
err := checkDownloadAndExtractLib(server.URL+"/project.tar.gz", destDir, "project-1.0.0")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to download and extract lib: %v", err)
|
||||
}
|
||||
|
||||
for name, expectedContent := range files {
|
||||
relPath := strings.TrimPrefix(name, "project-1.0.0/")
|
||||
filePath := filepath.Join(destDir, relPath)
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read extracted file %s: %v", relPath, err)
|
||||
continue
|
||||
}
|
||||
if string(content) != expectedContent {
|
||||
t.Errorf("File %s: expected content %q, got %q", relPath, expectedContent, string(content))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IncorrectInternalDir", func(t *testing.T) {
|
||||
os.RemoveAll(destDir)
|
||||
|
||||
err := checkDownloadAndExtractLib(server.URL+"/project.tar.gz", destDir, "wrong-dir")
|
||||
if err == nil {
|
||||
t.Error("Expected error for missing internal dir, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Mock test for WASI SDK (without actual download)
|
||||
func TestWasiSDKExtractionLogic(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
@@ -490,3 +649,130 @@ func TestESPClangDownloadWhenNotExists(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractZip(t *testing.T) {
|
||||
// Create temporary test directory
|
||||
tempDir := t.TempDir()
|
||||
zipPath := filepath.Join(tempDir, "test.zip")
|
||||
destDir := filepath.Join(tempDir, "extracted")
|
||||
|
||||
// 1. Test successful extraction
|
||||
t.Run("SuccessfulExtraction", func(t *testing.T) {
|
||||
// Create test ZIP file
|
||||
if err := createTestZip(zipPath); err != nil {
|
||||
t.Fatalf("Failed to create test zip: %v", err)
|
||||
}
|
||||
|
||||
// Execute extraction
|
||||
if err := extractZip(zipPath, destDir); err != nil {
|
||||
t.Fatalf("extractZip failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify extraction results
|
||||
verifyExtraction(t, destDir)
|
||||
})
|
||||
|
||||
// 2. Test invalid ZIP file
|
||||
t.Run("InvalidZipFile", func(t *testing.T) {
|
||||
// Create invalid ZIP file (actually a text file)
|
||||
if err := os.WriteFile(zipPath, []byte("not a zip file"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Execute extraction and expect error
|
||||
if err := extractZip(zipPath, destDir); err == nil {
|
||||
t.Error("Expected error for invalid zip file, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
// 3. Test non-writable destination
|
||||
t.Run("UnwritableDestination", func(t *testing.T) {
|
||||
// Create test ZIP file
|
||||
if err := createTestZip(zipPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create read-only destination directory
|
||||
readOnlyDir := filepath.Join(tempDir, "readonly")
|
||||
if err := os.MkdirAll(readOnlyDir, 0400); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Execute extraction and expect error
|
||||
if err := extractZip(zipPath, readOnlyDir); err == nil {
|
||||
t.Error("Expected error for unwritable destination, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create test ZIP file
|
||||
func createTestZip(zipPath string) error {
|
||||
// Create ZIP file
|
||||
zipFile, err := os.Create(zipPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
zipWriter := zip.NewWriter(zipFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
// Add directory
|
||||
dirHeader := &zip.FileHeader{
|
||||
Name: "testdir/",
|
||||
Method: zip.Deflate,
|
||||
Modified: time.Now(),
|
||||
}
|
||||
dirHeader.SetMode(os.ModeDir | 0755)
|
||||
if _, err := zipWriter.CreateHeader(dirHeader); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add file1
|
||||
file1, err := zipWriter.Create("file1.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := file1.Write([]byte("Hello from file1")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add nested file
|
||||
nestedFile, err := zipWriter.Create("testdir/nested.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := nestedFile.Write([]byte("Nested content")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify extraction results
|
||||
func verifyExtraction(t *testing.T, destDir string) {
|
||||
// Verify directory exists
|
||||
if _, err := os.Stat(filepath.Join(destDir, "testdir")); err != nil {
|
||||
t.Errorf("Directory not extracted: %v", err)
|
||||
}
|
||||
|
||||
// Verify file1 content
|
||||
file1Path := filepath.Join(destDir, "file1.txt")
|
||||
content, err := os.ReadFile(file1Path)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read file1: %v", err)
|
||||
}
|
||||
if string(content) != "Hello from file1" {
|
||||
t.Errorf("File1 content mismatch. Got: %s", content)
|
||||
}
|
||||
|
||||
// Verify nested file content
|
||||
nestedPath := filepath.Join(destDir, "testdir", "nested.txt")
|
||||
content, err = os.ReadFile(nestedPath)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to read nested file: %v", err)
|
||||
}
|
||||
if string(content) != "Nested content" {
|
||||
t.Errorf("Nested file content mismatch. Got: %s", content)
|
||||
}
|
||||
}
|
||||
|
||||
42
internal/crosscompile/libc.go
Normal file
42
internal/crosscompile/libc.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package crosscompile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile"
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile/libc"
|
||||
"github.com/goplus/llgo/internal/crosscompile/compile/rtlib"
|
||||
)
|
||||
|
||||
// GetCompileConfigByName retrieves libc compilation configuration by name
|
||||
// Returns compilation file lists and corresponding cflags
|
||||
func getLibcCompileConfigByName(baseDir, libcName, target, mcpu string) (*compile.CompileConfig, error) {
|
||||
if libcName == "" {
|
||||
return nil, fmt.Errorf("libc name cannot be empty")
|
||||
}
|
||||
libcDir := filepath.Join(baseDir, libcName)
|
||||
|
||||
switch libcName {
|
||||
case "picolibc":
|
||||
return libc.GetPicolibcConfig(libcDir, target), nil
|
||||
case "newlib-esp32":
|
||||
return libc.GetNewlibESP32Config(libcDir, target, mcpu), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported libc: %s", libcName)
|
||||
}
|
||||
}
|
||||
|
||||
func getRTCompileConfigByName(baseDir, rtName, target string) (*compile.CompileConfig, error) {
|
||||
if rtName == "" {
|
||||
return nil, fmt.Errorf("rt name cannot be empty")
|
||||
}
|
||||
rtDir := filepath.Join(baseDir, rtName)
|
||||
|
||||
switch rtName {
|
||||
case "compiler-rt":
|
||||
return rtlib.GetCompilerRTConfig(rtDir, target), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported rt: %s", rtName)
|
||||
}
|
||||
}
|
||||
109
internal/crosscompile/libc_test.go
Normal file
109
internal/crosscompile/libc_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
//go:build !llgo
|
||||
|
||||
package crosscompile
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetLibcCompileConfigByName(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "armv7"
|
||||
mcpu := "cortex-m4"
|
||||
|
||||
t.Run("EmptyName", func(t *testing.T) {
|
||||
_, err := getLibcCompileConfigByName(baseDir, "", target, mcpu)
|
||||
if err == nil || err.Error() != "libc name cannot be empty" {
|
||||
t.Errorf("Expected empty name error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UnsupportedLibc", func(t *testing.T) {
|
||||
_, err := getLibcCompileConfigByName(baseDir, "invalid", target, mcpu)
|
||||
if err == nil || err.Error() != "unsupported libc: invalid" {
|
||||
t.Errorf("Expected unsupported libc error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Picolibc", func(t *testing.T) {
|
||||
cfg, err := getLibcCompileConfigByName(baseDir, "picolibc", target, mcpu)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Groups) != 1 {
|
||||
t.Fatalf("Expected 1 group, got %d", len(cfg.Groups))
|
||||
}
|
||||
group := cfg.Groups[0]
|
||||
|
||||
expectedFile := filepath.Join(baseDir, "picolibc", "newlib", "libc", "string", "memmem.c")
|
||||
if !slices.Contains(group.Files, expectedFile) {
|
||||
t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files)
|
||||
}
|
||||
|
||||
expectedFlag := "-I" + filepath.Join("/test", "base", "picolibc")
|
||||
if !slices.Contains(group.CFlags, expectedFlag) {
|
||||
t.Errorf("Expected flags [%s], got: %v", expectedFlag, group.CFlags)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("NewlibESP32", func(t *testing.T) {
|
||||
cfg, err := getLibcCompileConfigByName(baseDir, "newlib-esp32", target, mcpu)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Groups) != 3 {
|
||||
t.Fatalf("Expected 3 group, got %d", len(cfg.Groups))
|
||||
}
|
||||
group := cfg.Groups[0]
|
||||
|
||||
expectedFile := filepath.Join(baseDir, "newlib-esp32", "libgloss", "xtensa", "crt1-boards.S")
|
||||
if !slices.Contains(group.Files, expectedFile) {
|
||||
t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files)
|
||||
}
|
||||
|
||||
expectedFlags := "-I" + filepath.Join(baseDir, "newlib-esp32", "libgloss")
|
||||
if !slices.Contains(group.CFlags, expectedFlags) {
|
||||
t.Errorf("Expected flags %v, got: %v", expectedFlags, group.CFlags)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRTCompileConfigByName(t *testing.T) {
|
||||
baseDir := "/test/base"
|
||||
target := "wasm32"
|
||||
|
||||
t.Run("EmptyName", func(t *testing.T) {
|
||||
_, err := getRTCompileConfigByName(baseDir, "", target)
|
||||
if err == nil || err.Error() != "rt name cannot be empty" {
|
||||
t.Errorf("Expected empty name error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("UnsupportedRT", func(t *testing.T) {
|
||||
_, err := getRTCompileConfigByName(baseDir, "invalid", target)
|
||||
if err == nil || err.Error() != "unsupported rt: invalid" {
|
||||
t.Errorf("Expected unsupported rt error, got: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CompilerRT", func(t *testing.T) {
|
||||
cfg, err := getRTCompileConfigByName(baseDir, "compiler-rt", target)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Groups) != 1 {
|
||||
t.Fatalf("Expected 1 group, got %d", len(cfg.Groups))
|
||||
}
|
||||
group := cfg.Groups[0]
|
||||
|
||||
expectedFile := filepath.Join(baseDir, "compiler-rt", "lib", "builtins", "absvdi2.c")
|
||||
if !slices.Contains(group.Files, expectedFile) {
|
||||
t.Errorf("Expected files [%s], got: %v", expectedFile, group.Files)
|
||||
}
|
||||
})
|
||||
}
|
||||
4
internal/env/env.go
vendored
4
internal/env/env.go
vendored
@@ -97,12 +97,12 @@ func isLLGoRoot(root string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
// Check for go.mod
|
||||
data, err := os.ReadFile(filepath.Join(root, "go.mod"))
|
||||
data, err := os.ReadFile(filepath.Join(root, LLGoRuntimePkgName, "go.mod"))
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
// Check module name
|
||||
if !strings.Contains(string(data), "module "+LLGoCompilerPkg+"\n") {
|
||||
if !strings.Contains(string(data), "module "+LLGoRuntimePkg+"\n") {
|
||||
return "", false
|
||||
}
|
||||
return root, true
|
||||
|
||||
18
internal/env/env_test.go
vendored
18
internal/env/env_test.go
vendored
@@ -41,8 +41,10 @@ func TestLLGoRuntimeDir(t *testing.T) {
|
||||
defer os.Setenv("LLGO_ROOT", origLLGoRoot)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
goModContent := []byte("module github.com/goplus/llgo\n")
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, "go.mod"), goModContent, 0644); err != nil {
|
||||
runtimeDir := filepath.Join(tmpDir, "runtime")
|
||||
os.MkdirAll(runtimeDir, 0755)
|
||||
goModContent := []byte("module github.com/goplus/llgo/runtime\n")
|
||||
if err := os.WriteFile(filepath.Join(runtimeDir, "go.mod"), goModContent, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -92,8 +94,10 @@ func TestLLGoROOT(t *testing.T) {
|
||||
defer os.Setenv("LLGO_ROOT", origLLGoRoot)
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
goModContent := []byte("module github.com/goplus/llgo\n")
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, "go.mod"), goModContent, 0644); err != nil {
|
||||
runtimeDir := filepath.Join(tmpDir, "runtime")
|
||||
os.MkdirAll(runtimeDir, 0755)
|
||||
goModContent := []byte("module github.com/goplus/llgo/runtime\n")
|
||||
if err := os.WriteFile(filepath.Join(runtimeDir, "go.mod"), goModContent, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -170,8 +174,10 @@ func TestIsLLGoRoot(t *testing.T) {
|
||||
// Test with valid path and valid go.mod
|
||||
t.Run("valid path and go.mod", func(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
goModContent := []byte("module github.com/goplus/llgo\n")
|
||||
if err := os.WriteFile(filepath.Join(tmpDir, "go.mod"), goModContent, 0644); err != nil {
|
||||
runtimeDir := filepath.Join(tmpDir, "runtime")
|
||||
os.MkdirAll(runtimeDir, 0755)
|
||||
goModContent := []byte("module github.com/goplus/llgo/runtime\n")
|
||||
if err := os.WriteFile(filepath.Join(runtimeDir, "go.mod"), goModContent, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ type Config struct {
|
||||
GOARCH string `json:"goarch"`
|
||||
|
||||
// Compiler and linker configuration
|
||||
Libc string `json:"libc"`
|
||||
RTLib string `json:"rtlib"`
|
||||
Linker string `json:"linker"`
|
||||
LinkerScript string `json:"linkerscript"`
|
||||
CFlags []string `json:"cflags"`
|
||||
|
||||
@@ -134,6 +134,12 @@ func (l *Loader) mergeConfig(dst, src *Config) {
|
||||
if src.GOARCH != "" {
|
||||
dst.GOARCH = src.GOARCH
|
||||
}
|
||||
if src.Libc != "" {
|
||||
dst.Libc = src.Libc
|
||||
}
|
||||
if src.RTLib != "" {
|
||||
dst.RTLib = src.RTLib
|
||||
}
|
||||
if src.Linker != "" {
|
||||
dst.Linker = src.Linker
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ package targets
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/goplus/llgo/internal/env"
|
||||
)
|
||||
|
||||
// Resolver provides high-level interface for target configuration resolution
|
||||
@@ -20,10 +21,8 @@ func NewResolver(targetsDir string) *Resolver {
|
||||
|
||||
// NewDefaultResolver creates a resolver with default targets directory
|
||||
func NewDefaultResolver() *Resolver {
|
||||
// Assume targets directory is relative to this package
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
projectRoot := filepath.Dir(filepath.Dir(filepath.Dir(filename)))
|
||||
targetsDir := filepath.Join(projectRoot, "targets")
|
||||
llgoRoot := env.LLGoROOT()
|
||||
targetsDir := filepath.Join(llgoRoot, "targets")
|
||||
|
||||
return NewResolver(targetsDir)
|
||||
}
|
||||
|
||||
17
runtime/internal/clite/os/dir.go
Normal file
17
runtime/internal/clite/os/dir.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
c "github.com/goplus/llgo/runtime/internal/clite"
|
||||
)
|
||||
|
||||
type DIR struct {
|
||||
Unused [0]byte
|
||||
}
|
||||
|
||||
//go:linkname Opendir C.opendir
|
||||
func Opendir(name *c.Char) *DIR
|
||||
|
||||
//go:linkname Closedir C.closedir
|
||||
func Closedir(dir *DIR) c.Int
|
||||
17
runtime/internal/clite/os/dir_darwin_amd64.go
Normal file
17
runtime/internal/clite/os/dir_darwin_amd64.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package os
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
c "github.com/goplus/llgo/runtime/internal/clite"
|
||||
"github.com/goplus/llgo/runtime/internal/clite/syscall"
|
||||
)
|
||||
|
||||
//go:linkname Fdopendir C.fdopendir$INODE64
|
||||
func Fdopendir(fd c.Int) *DIR
|
||||
|
||||
//go:linkname Readdir C.readdir$INODE64
|
||||
func Readdir(dir *DIR) *syscall.Dirent
|
||||
|
||||
//go:linkname Fstatat C.fstatat$INODE64
|
||||
func Fstatat(dirfd c.Int, path *c.Char, buf *StatT, flags c.Int) c.Int
|
||||
20
runtime/internal/clite/os/dir_unix.go
Normal file
20
runtime/internal/clite/os/dir_unix.go
Normal file
@@ -0,0 +1,20 @@
|
||||
//go:build !(darwin && amd64)
|
||||
// +build !darwin !amd64
|
||||
|
||||
package os
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
|
||||
c "github.com/goplus/llgo/runtime/internal/clite"
|
||||
"github.com/goplus/llgo/runtime/internal/clite/syscall"
|
||||
)
|
||||
|
||||
//go:linkname Fdopendir C.fdopendir
|
||||
func Fdopendir(fd c.Int) *DIR
|
||||
|
||||
//go:linkname Readdir C.readdir
|
||||
func Readdir(dir *DIR) *syscall.Dirent
|
||||
|
||||
//go:linkname Fstatat C.fstatat
|
||||
func Fstatat(dirfd c.Int, path *c.Char, buf *StatT, flags c.Int) c.Int
|
||||
@@ -135,9 +135,6 @@ func Fchmodat(dirfd c.Int, path *c.Char, mode ModeT, flags c.Int) c.Int
|
||||
//go:linkname Fchownat C.fchownat
|
||||
func Fchownat(dirfd c.Int, path *c.Char, owner UidT, group GidT, flags c.Int) c.Int
|
||||
|
||||
//go:linkname Fstatat C.fstatat
|
||||
func Fstatat(dirfd c.Int, path *c.Char, buf *StatT, flags c.Int) c.Int
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
//go:linkname Open C.open
|
||||
@@ -191,9 +188,6 @@ func Fchmod(fd c.Int, mode ModeT) c.Int
|
||||
//go:linkname Fchown C.fchown
|
||||
func Fchown(fd c.Int, owner UidT, group GidT) c.Int
|
||||
|
||||
//go:linkname Fstat C.fstat
|
||||
func Fstat(fd c.Int, buf *StatT) c.Int
|
||||
|
||||
//go:linkname Isatty C.isatty
|
||||
func Isatty(fd c.Int) c.Int
|
||||
|
||||
|
||||
@@ -14,3 +14,6 @@ func Stat(path *c.Char, buf *StatT) c.Int
|
||||
|
||||
//go:linkname Lstat C.lstat
|
||||
func Lstat(path *c.Char, buf *StatT) c.Int
|
||||
|
||||
//go:linkname Fstat C.fstat
|
||||
func Fstat(fd c.Int, buf *StatT) c.Int
|
||||
|
||||
@@ -11,3 +11,6 @@ func Stat(path *c.Char, buf *StatT) c.Int
|
||||
|
||||
//go:linkname Lstat C.lstat64
|
||||
func Lstat(path *c.Char, buf *StatT) c.Int
|
||||
|
||||
//go:linkname Fstat C.fstat64
|
||||
func Fstat(fd c.Int, buf *StatT) c.Int
|
||||
|
||||
@@ -82,30 +82,10 @@ func ReadDir(name string) ([]DirEntry, error) {
|
||||
return dirs, err
|
||||
}
|
||||
|
||||
//go:linkname c_fdopendir C.fdopendir
|
||||
func c_fdopendir(fd c.Int) uintptr
|
||||
|
||||
func fdopendir(fd int) (dir uintptr, err error) {
|
||||
return c_fdopendir(c.Int(fd)), nil
|
||||
}
|
||||
|
||||
//go:linkname c_closedir C.closedir
|
||||
func c_closedir(dir uintptr) c.Int
|
||||
|
||||
func closedir(dir uintptr) error {
|
||||
if c_closedir(dir) != 0 {
|
||||
return syscall.Errno(os.Errno())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:linkname c_readdir C.readdir
|
||||
func c_readdir(dir uintptr) *syscall.Dirent
|
||||
|
||||
func readdir(dir uintptr) ([]syscall.Dirent, error) {
|
||||
func readdir(dir *os.DIR) ([]syscall.Dirent, error) {
|
||||
var entries []syscall.Dirent
|
||||
for {
|
||||
dirent := c_readdir(dir)
|
||||
dirent := os.Readdir(dir)
|
||||
if dirent == nil {
|
||||
break
|
||||
}
|
||||
@@ -139,11 +119,11 @@ func (f *File) ReadDir(n int) (dirents []DirEntry, err error) {
|
||||
}
|
||||
|
||||
// Open directory using file descriptor
|
||||
dir, err := fdopendir(int(f.fd))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
dir := os.Fdopendir(c.Int(f.fd))
|
||||
if dir == nil {
|
||||
return nil, syscall.Errno(os.Errno())
|
||||
}
|
||||
defer closedir(dir)
|
||||
defer os.Closedir(dir)
|
||||
|
||||
// Match Readdir and Readdirnames: don't return nil slices.
|
||||
dirents = []DirEntry{}
|
||||
|
||||
12
ssa/expr.go
12
ssa/expr.go
@@ -286,6 +286,18 @@ func (b Builder) InlineAsm(instruction string) {
|
||||
b.impl.CreateCall(typ, asm, nil, "")
|
||||
}
|
||||
|
||||
func (b Builder) InlineAsmFull(instruction, constraints string, retType Type, exprs []Expr) Expr {
|
||||
typs := make([]llvm.Type, len(exprs))
|
||||
vals := make([]llvm.Value, len(exprs))
|
||||
for i, expr := range exprs {
|
||||
typs[i], vals[i] = expr.Type.ll, expr.impl
|
||||
}
|
||||
|
||||
ftype := llvm.FunctionType(retType.ll, typs, false)
|
||||
asm := llvm.InlineAsm(ftype, instruction, constraints, true, false, llvm.InlineAsmDialectATT, false)
|
||||
return Expr{b.impl.CreateCall(ftype, asm, vals, ""), retType}
|
||||
}
|
||||
|
||||
// GoString returns a Go string
|
||||
func (b Builder) GoString(v Expr) Expr {
|
||||
fn := b.Pkg.rtFunc("GoString")
|
||||
|
||||
195
targets/esp32-riscv.app.elf.ld
Normal file
195
targets/esp32-riscv.app.elf.ld
Normal file
@@ -0,0 +1,195 @@
|
||||
__stack = ORIGIN(dram_seg) + LENGTH(dram_seg);
|
||||
__MIN_STACK_SIZE = 0x1000;
|
||||
_stack_top = __stack;
|
||||
|
||||
/* Default entry point */
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_iram_start = .;
|
||||
|
||||
/* Place the _start function at the beginning of IRAM.
|
||||
* Just in case some versions of QEMU ignore the entry address when loading the ELF file. */
|
||||
KEEP(*(.text._start))
|
||||
|
||||
KEEP (*(SORT_NONE(.init)))
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
KEEP (*(SORT_NONE(.fini)))
|
||||
PROVIDE (__etext = .);
|
||||
PROVIDE (_etext = .);
|
||||
PROVIDE (etext = .);
|
||||
} > iram_seg
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
*(.rodata1)
|
||||
*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
|
||||
*(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
|
||||
. = ALIGN(4);
|
||||
__cpu_frequency = .;
|
||||
LONG(CPU_FREQUENCY);
|
||||
__uart0_clkdiv_reg = .;
|
||||
LONG(UART0_CLKDIV_REG);
|
||||
__uart0_clkdiv_val = .;
|
||||
LONG(UART0_CLKDIV_VAL);
|
||||
__uart0_tx_addr = .;
|
||||
LONG(UART0_TX_ADDR);
|
||||
__uart0_status = .;
|
||||
LONG(UART0_STATUS);
|
||||
} > iram_seg
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} > iram_seg
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} > iram_seg
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} > iram_seg
|
||||
.ctors :
|
||||
{
|
||||
/* gcc uses crtbegin.o to find the start of
|
||||
the constructors, so we make sure it is
|
||||
first. Because this is a wildcard, it
|
||||
doesn't matter if the user does not
|
||||
actually link against crtbegin.o; the
|
||||
linker won't look for a file to match a
|
||||
wildcard. The wildcard also means that it
|
||||
doesn't matter which directory crtbegin.o
|
||||
is in. */
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*crtbegin?.o(.ctors))
|
||||
/* We don't want to include the .ctor section from
|
||||
the crtend.o file until after the sorted ctors.
|
||||
The .ctor section from the crtend file contains the
|
||||
end of ctors marker and it must be last */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} > iram_seg
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*crtbegin?.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
_iram_end = .;
|
||||
} > iram_seg
|
||||
|
||||
/**
|
||||
* This section is required to skip .iram0.text area because iram0_0_seg and
|
||||
* dram0_0_seg reflect the same address space on different buses.
|
||||
*/
|
||||
.dram0.dummy (NOLOAD):
|
||||
{
|
||||
/* Add a gap only in case we have separate iram/dram regions */
|
||||
. += ORIGIN(iram_seg) == ORIGIN(dram_seg) ? 0 : _iram_end - _iram_start;
|
||||
} > dram_seg
|
||||
|
||||
.data :
|
||||
{
|
||||
_data_start = .;
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
} > dram_seg
|
||||
.data1 : { *(.data1) } > dram_seg
|
||||
/* We want the small data sections together, so single-instruction offsets
|
||||
can access them all, and initialized data all before uninitialized, so
|
||||
we can shorten the on-disk segment size. */
|
||||
.sdata :
|
||||
{
|
||||
PROVIDE(__global_pointer$ = . + 0x800);
|
||||
*(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
|
||||
*(.sdata .sdata.* .gnu.linkonce.s.*)
|
||||
_edata = .; PROVIDE (edata = .);
|
||||
. = .;
|
||||
} > dram_seg
|
||||
|
||||
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } > dram_seg
|
||||
.eh_frame : { KEEP (*(.eh_frame)) *(.eh_frame.*) } > dram_seg
|
||||
|
||||
.bss (NOLOAD) :
|
||||
{
|
||||
__bss_start = .;
|
||||
*(.dynsbss)
|
||||
*(.sbss .sbss.* .gnu.linkonce.sb.*)
|
||||
*(.scommon)
|
||||
*(.dynbss)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
/* Align here to ensure that the .bss section occupies space up to
|
||||
_end. Align after .bss to ensure correct alignment even if the
|
||||
.bss section disappears because there are no input sections.
|
||||
FIXME: Why do we need it? When there is no .bss section, we do not
|
||||
pad the .data section. */
|
||||
. = ALIGN(. != 0 ? 32 / 8 : 1);
|
||||
|
||||
. = ALIGN(32 / 8);
|
||||
. = ALIGN(32 / 8);
|
||||
_end = .; PROVIDE (end = .);
|
||||
} > dram_seg
|
||||
|
||||
/* Check if data + heap + stack exceeds RAM limit */
|
||||
ASSERT(_end <= __stack - __MIN_STACK_SIZE, "region DRAM overflowed by .data and .bss sections")
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
.gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* DWARF 3 */
|
||||
.debug_pubtypes 0 : { *(.debug_pubtypes) }
|
||||
.debug_ranges 0 : { *(.debug_ranges) }
|
||||
/* DWARF Extension. */
|
||||
.debug_macro 0 : { *(.debug_macro) }
|
||||
.debug_addr 0 : { *(.debug_addr) }
|
||||
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
|
||||
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
|
||||
}
|
||||
185
targets/esp32.app.elf.ld
Executable file
185
targets/esp32.app.elf.ld
Executable file
@@ -0,0 +1,185 @@
|
||||
__stack = ORIGIN(dram_seg) + LENGTH(dram_seg);
|
||||
__MIN_STACK_SIZE = 0x2000;
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = SEGMENT_START("iram_seg", 0);
|
||||
.vectors :
|
||||
{
|
||||
_vector_table = ABSOLUTE(.);
|
||||
KEEP(*(.WindowVectors.text));
|
||||
KEEP(*(.Level2InterruptVector.text));
|
||||
KEEP(*(.Level3InterruptVector.text));
|
||||
KEEP(*(.Level4InterruptVector.text));
|
||||
KEEP(*(.Level5InterruptVector.text));
|
||||
KEEP(*(.DebugExceptionVector.text));
|
||||
KEEP(*(.NMIExceptionVector.text));
|
||||
KEEP(*(.KernelExceptionVector.text));
|
||||
KEEP(*(.UserExceptionVector.text));
|
||||
KEEP(*(.DoubleExceptionVector.text));
|
||||
KEEP(*(.ResetVector.text));
|
||||
*(.*Vector.literal)
|
||||
. = ALIGN (16);
|
||||
} > iram_seg
|
||||
|
||||
text :
|
||||
{
|
||||
KEEP (*(.init.literal))
|
||||
KEEP (*(SORT_NONE(.init)))
|
||||
*(.literal .text .stub .literal.* .text.* .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
|
||||
/* .gnu.warning sections are handled specially by elf32.em. */
|
||||
*(.gnu.warning)
|
||||
KEEP (*(.fini.literal))
|
||||
KEEP (*(SORT_NONE(.fini)))
|
||||
} > iram_seg
|
||||
|
||||
PROVIDE (__etext = .);
|
||||
PROVIDE (_etext = .);
|
||||
PROVIDE (etext = .);
|
||||
|
||||
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
|
||||
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
*(.rodata1)
|
||||
*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
|
||||
*(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
|
||||
}
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
}
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
}
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
}
|
||||
.ctors :
|
||||
{
|
||||
/* gcc uses crtbegin.o to find the start of
|
||||
the constructors, so we make sure it is
|
||||
first. Because this is a wildcard, it
|
||||
doesn't matter if the user does not
|
||||
actually link against crtbegin.o; the
|
||||
linker won't look for a file to match a
|
||||
wildcard. The wildcard also means that it
|
||||
doesn't matter which directory crtbegin.o
|
||||
is in. */
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*crtbegin?.o(.ctors))
|
||||
/* We don't want to include the .ctor section from
|
||||
the crtend.o file until after the sorted ctors.
|
||||
The .ctor section from the crtend file contains the
|
||||
end of ctors marker and it must be last */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
}
|
||||
.dtors :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*crtbegin?.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
}
|
||||
|
||||
_data_start = .;
|
||||
.data :
|
||||
{
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
*(.data1)
|
||||
}
|
||||
_edata = .; PROVIDE (edata = .);
|
||||
. = .;
|
||||
__bss_start = .;
|
||||
.bss :
|
||||
{
|
||||
*(.dynsbss)
|
||||
*(.sbss .sbss.* .gnu.linkonce.sb.*)
|
||||
*(.scommon)
|
||||
*(.dynbss)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
/* Align here to ensure that the .bss section occupies space up to
|
||||
_end. Align after .bss to ensure correct alignment even if the
|
||||
.bss section disappears because there are no input sections.
|
||||
FIXME: Why do we need it? When there is no .bss section, we do not
|
||||
pad the .data section. */
|
||||
. = ALIGN(. != 0 ? 32 / 8 : 1);
|
||||
}
|
||||
. = ALIGN(32 / 8);
|
||||
. = ALIGN(32 / 8);
|
||||
_end = .; PROVIDE (end = .);
|
||||
. = DATA_SEGMENT_END (.);
|
||||
|
||||
/* Check if data + heap + stack exceeds RAM limit */
|
||||
ASSERT(. <= __stack - __MIN_STACK_SIZE, "region DRAM overflowed by .data and .bss sections")
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
.gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/* DWARF 3 */
|
||||
.debug_pubtypes 0 : { *(.debug_pubtypes) }
|
||||
.debug_ranges 0 : { *(.debug_ranges) }
|
||||
/* DWARF Extension. */
|
||||
.debug_macro 0 : { *(.debug_macro) }
|
||||
.debug_addr 0 : { *(.debug_addr) }
|
||||
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
|
||||
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
|
||||
}
|
||||
|
||||
|
||||
_sbss = __bss_start;
|
||||
_ebss = _end;
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
{
|
||||
"inherits": ["xtensa"],
|
||||
"inherits": [
|
||||
"xtensa"
|
||||
],
|
||||
"cpu": "esp32",
|
||||
"features": "+atomctl,+bool,+clamps,+coprocessor,+debug,+density,+dfpaccel,+div32,+exception,+fp,+highpriinterrupts,+interrupt,+loop,+mac16,+memctl,+minmax,+miscsr,+mul32,+mul32high,+nsa,+prid,+regprotect,+rvector,+s32c1i,+sext,+threadptr,+timerint,+windowed",
|
||||
"build-tags": ["esp32", "esp"],
|
||||
"build-tags": [
|
||||
"esp32",
|
||||
"esp"
|
||||
],
|
||||
"scheduler": "tasks",
|
||||
"serial": "uart",
|
||||
"linker": "ld.lld",
|
||||
"default-stack-size": 2048,
|
||||
"rtlib": "compiler-rt",
|
||||
"libc": "picolibc",
|
||||
"linkerscript": "targets/esp32.ld",
|
||||
"extra-files": [
|
||||
"targets/device/esp/esp32.S"
|
||||
],
|
||||
"libc": "newlib-esp32",
|
||||
"linkerscript": "targets/esp32.memory.elf.ld",
|
||||
"extra-files": [],
|
||||
"binary-format": "esp32",
|
||||
"flash-command": "esptool.py --chip=esp32 --port {port} write_flash 0x1000 {bin} -ff 80m -fm dout",
|
||||
"emulator": "qemu-system-xtensa -machine esp32 -nographic -drive file={img},if=mtd,format=raw",
|
||||
"gdb": ["xtensa-esp32-elf-gdb"]
|
||||
"gdb": [
|
||||
"xtensa-esp32-elf-gdb"
|
||||
]
|
||||
}
|
||||
|
||||
202
targets/esp32.ld
202
targets/esp32.ld
@@ -1,202 +0,0 @@
|
||||
/* Linker script for the ESP32 */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
/* Data RAM. Allows byte access.
|
||||
* There are various data RAM regions:
|
||||
* SRAM2: 0x3FFA_E000..0x3FFD_FFFF (72 + 128 = 200K)
|
||||
* SRAM1: 0x3FFE_0000..0x3FFF_FFFF (128K)
|
||||
* This gives us 328K of contiguous RAM, which is the largest span possible.
|
||||
* SRAM1 has other addresses as well but the datasheet seems to indicate
|
||||
* these are aliases.
|
||||
*/
|
||||
DRAM (rw) : ORIGIN = 0x3FFAE000, LENGTH = 200K + 128K /* Internal SRAM 1 + 2 */
|
||||
|
||||
/* Instruction RAM. */
|
||||
IRAM (x) : ORIGIN = 0x40080000, LENGTH = 128K /* Internal SRAM 0 */
|
||||
}
|
||||
|
||||
/* The entry point. It is set in the image flashed to the chip, so must be
|
||||
* defined.
|
||||
*/
|
||||
ENTRY(call_start_cpu0)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Constant literals and code. Loaded into IRAM for now. Eventually, most
|
||||
* code should be executed directly from flash.
|
||||
* Note that literals must be before code for the l32r instruction to work.
|
||||
*/
|
||||
.text : ALIGN(4)
|
||||
{
|
||||
*(.literal.call_start_cpu0)
|
||||
*(.text.call_start_cpu0)
|
||||
*(.literal .text)
|
||||
*(.literal.* .text.*)
|
||||
} >IRAM
|
||||
|
||||
/* Put the stack at the bottom of DRAM, so that the application will
|
||||
* crash on stack overflow instead of silently corrupting memory.
|
||||
* See: http://blog.japaric.io/stack-overflow-protection/ */
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(16);
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
} >DRAM
|
||||
|
||||
/* Constant global variables.
|
||||
* They are loaded in DRAM for ease of use. Eventually they should be stored
|
||||
* in flash and loaded directly from there but they're kept in RAM to make
|
||||
* sure they can always be accessed (even in interrupts).
|
||||
*/
|
||||
.rodata : ALIGN(4)
|
||||
{
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >DRAM
|
||||
|
||||
/* Mutable global variables.
|
||||
*/
|
||||
.data : ALIGN(4)
|
||||
{
|
||||
_sdata = ABSOLUTE(.);
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
_edata = ABSOLUTE(.);
|
||||
} >DRAM
|
||||
|
||||
/* Check that the boot ROM stack (for the APP CPU) does not overlap with the
|
||||
* data that is loaded by the boot ROM. There may be ways to avoid this
|
||||
* issue if it occurs in practice.
|
||||
* The magic value here is _stack_sentry in the boot ROM ELF file.
|
||||
*/
|
||||
ASSERT(_edata < 0x3ffe1320, "the .data section overlaps with the stack used by the boot ROM, possibly causing corruption at startup")
|
||||
|
||||
/* Global variables that are mutable and zero-initialized.
|
||||
* These must be zeroed at startup (unlike data, which is loaded by the
|
||||
* bootloader).
|
||||
*/
|
||||
.bss (NOLOAD) : ALIGN(4)
|
||||
{
|
||||
. = ALIGN (4);
|
||||
_sbss = ABSOLUTE(.);
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
. = ALIGN (4);
|
||||
_ebss = ABSOLUTE(.);
|
||||
} >DRAM
|
||||
}
|
||||
|
||||
/* For the garbage collector.
|
||||
*/
|
||||
_globals_start = _sdata;
|
||||
_globals_end = _ebss;
|
||||
_heap_start = _ebss;
|
||||
_heap_end = ORIGIN(DRAM) + LENGTH(DRAM);
|
||||
|
||||
_stack_size = 4K;
|
||||
|
||||
/* From ESP-IDF:
|
||||
* components/esp_rom/esp32/ld/esp32.rom.newlib-funcs.ld
|
||||
* This is the subset that is sometimes used by LLVM during codegen, and thus
|
||||
* must always be present.
|
||||
*/
|
||||
memcpy = 0x4000c2c8;
|
||||
memmove = 0x4000c3c0;
|
||||
memset = 0x4000c44c;
|
||||
|
||||
/* From ESP-IDF:
|
||||
* components/esp_rom/esp32/ld/esp32.rom.libgcc.ld
|
||||
* These are called from LLVM during codegen. The original license is Apache
|
||||
* 2.0, but I believe that a list of function names and addresses can't really
|
||||
* be copyrighted.
|
||||
*/
|
||||
__absvdi2 = 0x4006387c;
|
||||
__absvsi2 = 0x40063868;
|
||||
__adddf3 = 0x40002590;
|
||||
__addsf3 = 0x400020e8;
|
||||
__addvdi3 = 0x40002cbc;
|
||||
__addvsi3 = 0x40002c98;
|
||||
__ashldi3 = 0x4000c818;
|
||||
__ashrdi3 = 0x4000c830;
|
||||
__bswapdi2 = 0x40064b08;
|
||||
__bswapsi2 = 0x40064ae0;
|
||||
__clrsbdi2 = 0x40064b7c;
|
||||
__clrsbsi2 = 0x40064b64;
|
||||
__clzdi2 = 0x4000ca50;
|
||||
__clzsi2 = 0x4000c7e8;
|
||||
__cmpdi2 = 0x40063820;
|
||||
__ctzdi2 = 0x4000ca64;
|
||||
__ctzsi2 = 0x4000c7f0;
|
||||
__divdc3 = 0x400645a4;
|
||||
__divdf3 = 0x40002954;
|
||||
__divdi3 = 0x4000ca84;
|
||||
__divsi3 = 0x4000c7b8;
|
||||
__eqdf2 = 0x400636a8;
|
||||
__eqsf2 = 0x40063374;
|
||||
__extendsfdf2 = 0x40002c34;
|
||||
__ffsdi2 = 0x4000ca2c;
|
||||
__ffssi2 = 0x4000c804;
|
||||
__fixdfdi = 0x40002ac4;
|
||||
__fixdfsi = 0x40002a78;
|
||||
__fixsfdi = 0x4000244c;
|
||||
__fixsfsi = 0x4000240c;
|
||||
__fixunsdfsi = 0x40002b30;
|
||||
__fixunssfdi = 0x40002504;
|
||||
__fixunssfsi = 0x400024ac;
|
||||
__floatdidf = 0x4000c988;
|
||||
__floatdisf = 0x4000c8c0;
|
||||
__floatsidf = 0x4000c944;
|
||||
__floatsisf = 0x4000c870;
|
||||
__floatundidf = 0x4000c978;
|
||||
__floatundisf = 0x4000c8b0;
|
||||
__floatunsidf = 0x4000c938;
|
||||
__floatunsisf = 0x4000c864;
|
||||
__gcc_bcmp = 0x40064a70;
|
||||
__gedf2 = 0x40063768;
|
||||
__gesf2 = 0x4006340c;
|
||||
__gtdf2 = 0x400636dc;
|
||||
__gtsf2 = 0x400633a0;
|
||||
__ledf2 = 0x40063704;
|
||||
__lesf2 = 0x400633c0;
|
||||
__lshrdi3 = 0x4000c84c;
|
||||
__ltdf2 = 0x40063790;
|
||||
__ltsf2 = 0x4006342c;
|
||||
__moddi3 = 0x4000cd4c;
|
||||
__modsi3 = 0x4000c7c0;
|
||||
__muldc3 = 0x40063c90;
|
||||
__muldf3 = 0x4006358c;
|
||||
__muldi3 = 0x4000c9fc;
|
||||
__mulsf3 = 0x400632c8;
|
||||
__mulsi3 = 0x4000c7b0;
|
||||
__mulvdi3 = 0x40002d78;
|
||||
__mulvsi3 = 0x40002d60;
|
||||
__nedf2 = 0x400636a8;
|
||||
__negdf2 = 0x400634a0;
|
||||
__negdi2 = 0x4000ca14;
|
||||
__negsf2 = 0x400020c0;
|
||||
__negvdi2 = 0x40002e98;
|
||||
__negvsi2 = 0x40002e78;
|
||||
__nesf2 = 0x40063374;
|
||||
__nsau_data = 0x3ff96544;
|
||||
__paritysi2 = 0x40002f3c;
|
||||
__popcount_tab = 0x3ff96544;
|
||||
__popcountdi2 = 0x40002ef8;
|
||||
__popcountsi2 = 0x40002ed0;
|
||||
__powidf2 = 0x400638e4;
|
||||
__subdf3 = 0x400026e4;
|
||||
__subsf3 = 0x400021d0;
|
||||
__subvdi3 = 0x40002d20;
|
||||
__subvsi3 = 0x40002cf8;
|
||||
__truncdfsf2 = 0x40002b90;
|
||||
__ucmpdi2 = 0x40063840;
|
||||
__udiv_w_sdiv = 0x40064bec;
|
||||
__udivdi3 = 0x4000cff8;
|
||||
__udivmoddi4 = 0x40064bf4;
|
||||
__udivsi3 = 0x4000c7c8;
|
||||
__umoddi3 = 0x4000d280;
|
||||
__umodsi3 = 0x4000c7d0;
|
||||
__umulsidi3 = 0x4000c7d8;
|
||||
__unorddf2 = 0x400637f4;
|
||||
__unordsf2 = 0x40063478;
|
||||
26
targets/esp32.memory.elf.ld
Executable file
26
targets/esp32.memory.elf.ld
Executable file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* IROM/DRAM definition in QEMU:
|
||||
* [ESP32_MEMREGION_IROM] = { 0x40000000, 0x70000 },
|
||||
* [ESP32_MEMREGION_DRAM] = { 0x3ffae000, 0x52000 },
|
||||
*
|
||||
* In theory we could use whole DRAM section, but I had some faults when using
|
||||
* memory in range 0x3ffae000 - 0x3ffb0000
|
||||
*
|
||||
* But used memory range for data such as esp-idf for ESP32 to satisfy user's
|
||||
* expectation on chip emulation
|
||||
*
|
||||
* Pass '--defsym=entire_dram_seg=1' to linker script to use whole DRAM
|
||||
*
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
iram_seg (X) : org = 0x40078000, len = 0x28000
|
||||
/* 64k at the end of DRAM, after ROM bootloader stack
|
||||
* or entire DRAM (for QEMU only)
|
||||
*/
|
||||
dram_seg (RW) : org = 0x3FFF0000 ,
|
||||
len = 0x10000
|
||||
}
|
||||
|
||||
INCLUDE "targets/esp32.app.elf.ld";
|
||||
33
targets/esp32c2.memory.ld
Normal file
33
targets/esp32c2.memory.ld
Normal file
@@ -0,0 +1,33 @@
|
||||
# See "System Structure and Address Mapping" figure at
|
||||
# https://www.espressif.com/sites/default/files/documentation/esp8684_technical_reference_manual_en.pdf
|
||||
|
||||
ICACHE_SIZE = 0x4000;
|
||||
# Skip possible ICACHE area
|
||||
IRAM_START_ADDRESS = 0x4037C000 + ICACHE_SIZE;
|
||||
IRAM_LEN = 0x40000 - ICACHE_SIZE;
|
||||
|
||||
DRAM_START_ADDRESS = 0x3FCA0000;
|
||||
DRAM_LEN = 0x40000;
|
||||
|
||||
# Docs say that:
|
||||
# The default console baud rate on ESP32-C2:
|
||||
# - 115200 when a 40 MHz XTAL is used
|
||||
# - 74880 when a 26 MHz XTAL is used
|
||||
#
|
||||
# It seems something wrong with CPU_FREQUENCY and UART0_BAUD definitions,
|
||||
# but UART0_CLKDIV_VAL == 173 gives expected baud rate 74880 on 26 MHz XTAL.
|
||||
CPU_FREQUENCY = 20000000;
|
||||
UART0_BAUD = 115200;
|
||||
|
||||
UART0_CLKDIV_REG = 0x60000014;
|
||||
UART0_CLKDIV_VAL = CPU_FREQUENCY / UART0_BAUD;
|
||||
UART0_STATUS = 0x6000001C;
|
||||
UART0_TX_ADDR = 0x60000000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
iram_seg (RX) : org = IRAM_START_ADDRESS, len = IRAM_LEN
|
||||
dram_seg (RW) : org = DRAM_START_ADDRESS, len = DRAM_LEN
|
||||
}
|
||||
|
||||
INCLUDE "targets/esp32-riscv.app.elf.ld";
|
||||
@@ -1,23 +1,31 @@
|
||||
{
|
||||
"inherits": ["riscv32"],
|
||||
"inherits": [
|
||||
"riscv32"
|
||||
],
|
||||
"features": "+32bit,+c,+m,+zmmul,-a,-b,-d,-e,-experimental-smmpm,-experimental-smnpm,-experimental-ssnpm,-experimental-sspm,-experimental-ssqosid,-experimental-supm,-experimental-zacas,-experimental-zalasr,-experimental-zicfilp,-experimental-zicfiss,-f,-h,-relax,-shcounterenw,-shgatpa,-shtvala,-shvsatpa,-shvstvala,-shvstvecd,-smaia,-smcdeleg,-smcsrind,-smepmp,-smstateen,-ssaia,-ssccfg,-ssccptr,-sscofpmf,-sscounterenw,-sscsrind,-ssstateen,-ssstrict,-sstc,-sstvala,-sstvecd,-ssu64xl,-svade,-svadu,-svbare,-svinval,-svnapot,-svpbmt,-v,-xcvalu,-xcvbi,-xcvbitmanip,-xcvelw,-xcvmac,-xcvmem,-xcvsimd,-xesppie,-xsfcease,-xsfvcp,-xsfvfnrclipxfqf,-xsfvfwmaccqqq,-xsfvqmaccdod,-xsfvqmaccqoq,-xsifivecdiscarddlone,-xsifivecflushdlone,-xtheadba,-xtheadbb,-xtheadbs,-xtheadcmo,-xtheadcondmov,-xtheadfmemidx,-xtheadmac,-xtheadmemidx,-xtheadmempair,-xtheadsync,-xtheadvdot,-xventanacondops,-xwchc,-za128rs,-za64rs,-zaamo,-zabha,-zalrsc,-zama16b,-zawrs,-zba,-zbb,-zbc,-zbkb,-zbkc,-zbkx,-zbs,-zca,-zcb,-zcd,-zce,-zcf,-zcmop,-zcmp,-zcmt,-zdinx,-zfa,-zfbfmin,-zfh,-zfhmin,-zfinx,-zhinx,-zhinxmin,-zic64b,-zicbom,-zicbop,-zicboz,-ziccamoa,-ziccif,-zicclsm,-ziccrse,-zicntr,-zicond,-zicsr,-zifencei,-zihintntl,-zihintpause,-zihpm,-zimop,-zk,-zkn,-zknd,-zkne,-zknh,-zkr,-zks,-zksed,-zksh,-zkt,-ztso,-zvbb,-zvbc,-zve32f,-zve32x,-zve64d,-zve64f,-zve64x,-zvfbfmin,-zvfbfwma,-zvfh,-zvfhmin,-zvkb,-zvkg,-zvkn,-zvknc,-zvkned,-zvkng,-zvknha,-zvknhb,-zvks,-zvksc,-zvksed,-zvksg,-zvksh,-zvkt,-zvl1024b,-zvl128b,-zvl16384b,-zvl2048b,-zvl256b,-zvl32768b,-zvl32b,-zvl4096b,-zvl512b,-zvl64b,-zvl65536b,-zvl8192b",
|
||||
"build-tags": ["esp32c3", "esp"],
|
||||
"build-tags": [
|
||||
"esp32c3",
|
||||
"esp"
|
||||
],
|
||||
"serial": "usb",
|
||||
"rtlib": "compiler-rt",
|
||||
"libc": "picolibc",
|
||||
"libc": "newlib-esp32",
|
||||
"cflags": [
|
||||
"-march=rv32imc"
|
||||
],
|
||||
"linkerscript": "targets/esp32c3.ld",
|
||||
"extra-files": [
|
||||
"targets/device/esp/esp32c3.S"
|
||||
],
|
||||
"linkerscript": "targets/esp32c3.memory.ld",
|
||||
"extra-files": [],
|
||||
"binary-format": "esp32c3",
|
||||
"flash-command": "esptool.py --chip=esp32c3 --port {port} write_flash 0x0 {bin}",
|
||||
"serial-port": ["303a:1001"],
|
||||
"serial-port": [
|
||||
"303a:1001"
|
||||
],
|
||||
"openocd-interface": "esp_usb_jtag",
|
||||
"openocd-target": "esp32c3",
|
||||
"openocd-commands": ["gdb_memory_map disable"],
|
||||
"gdb": ["riscv32-esp-elf-gdb"]
|
||||
"openocd-commands": [
|
||||
"gdb_memory_map disable"
|
||||
],
|
||||
"gdb": [
|
||||
"riscv32-esp-elf-gdb"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
2238
targets/esp32c3.ld
2238
targets/esp32c3.ld
File diff suppressed because it is too large
Load Diff
26
targets/esp32c3.memory.ld
Normal file
26
targets/esp32c3.memory.ld
Normal file
@@ -0,0 +1,26 @@
|
||||
# See "System Structure and Address Mapping" figure at
|
||||
# https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf
|
||||
|
||||
ICACHE_SIZE = 0x4000;
|
||||
# Skip possible ICACHE area
|
||||
IRAM_START_ADDRESS = 0x4037C000 + ICACHE_SIZE;
|
||||
IRAM_LEN = 0x64000 - ICACHE_SIZE;
|
||||
|
||||
DRAM_START_ADDRESS = 0x3FC80000;
|
||||
DRAM_LEN = 0x40000;
|
||||
|
||||
CPU_FREQUENCY = 20000000;
|
||||
UART0_BAUD = 115200;
|
||||
|
||||
UART0_CLKDIV_REG = 0x60000014;
|
||||
UART0_CLKDIV_VAL = CPU_FREQUENCY / UART0_BAUD;
|
||||
UART0_STATUS = 0x6000001C;
|
||||
UART0_TX_ADDR = 0x60000000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
iram_seg (RX) : org = IRAM_START_ADDRESS, len = IRAM_LEN
|
||||
dram_seg (RW) : org = DRAM_START_ADDRESS, len = DRAM_LEN
|
||||
}
|
||||
|
||||
INCLUDE "targets/esp32-riscv.app.elf.ld";
|
||||
22
targets/esp32c5.memory.ld
Normal file
22
targets/esp32c5.memory.ld
Normal file
@@ -0,0 +1,22 @@
|
||||
# Memory layout obtained from IDF linker files.
|
||||
|
||||
SRAM_START_ADDRESS = 0x40800000;
|
||||
SRAM_LEN = 0x60000;
|
||||
|
||||
CPU_FREQUENCY = 40000000;
|
||||
UART0_BAUD = 115200;
|
||||
|
||||
UART0_CLKDIV_REG = 0x60000014;
|
||||
UART0_CLKDIV_VAL = CPU_FREQUENCY / UART0_BAUD;
|
||||
UART0_STATUS = 0x6000001C;
|
||||
UART0_TX_ADDR = 0x60000000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
sram_seg (RWX) : org = SRAM_START_ADDRESS, len = SRAM_LEN
|
||||
}
|
||||
|
||||
REGION_ALIAS("iram_seg", sram_seg);
|
||||
REGION_ALIAS("dram_seg", sram_seg);
|
||||
|
||||
INCLUDE "targets/esp32-riscv.app.elf.ld";
|
||||
22
targets/esp32c6.memory.ld
Normal file
22
targets/esp32c6.memory.ld
Normal file
@@ -0,0 +1,22 @@
|
||||
# See "System Structure and Address Mapping" figure at
|
||||
# https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf
|
||||
|
||||
SRAM_START_ADDRESS = 0x40800000;
|
||||
SRAM_LEN = 0x80000;
|
||||
|
||||
CPU_FREQUENCY = 40000000;
|
||||
UART0_BAUD = 115200;
|
||||
|
||||
UART0_CLKDIV_REG = 0x60000014;
|
||||
UART0_CLKDIV_VAL = CPU_FREQUENCY / UART0_BAUD;
|
||||
UART0_STATUS = 0x6000001C;
|
||||
UART0_TX_ADDR = 0x60000000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
sram_seg (RWX) : org = SRAM_START_ADDRESS, len = SRAM_LEN
|
||||
}
|
||||
|
||||
REGION_ALIAS("iram_seg", sram_seg);
|
||||
REGION_ALIAS("dram_seg", sram_seg);
|
||||
INCLUDE "targets/esp32-riscv.app.elf.ld";
|
||||
21
targets/esp32c61.memory.ld
Normal file
21
targets/esp32c61.memory.ld
Normal file
@@ -0,0 +1,21 @@
|
||||
# Memory layout obtained from IDF linker files.
|
||||
|
||||
SRAM_START_ADDRESS = 0x40800000;
|
||||
SRAM_LEN = 0x50000;
|
||||
|
||||
CPU_FREQUENCY = 40000000;
|
||||
UART0_BAUD = 115200;
|
||||
|
||||
UART0_CLKDIV_REG = 0x60000014;
|
||||
UART0_CLKDIV_VAL = CPU_FREQUENCY / UART0_BAUD;
|
||||
UART0_STATUS = 0x6000001C;
|
||||
UART0_TX_ADDR = 0x60000000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
sram_seg (RWX) : org = SRAM_START_ADDRESS, len = SRAM_LEN
|
||||
}
|
||||
|
||||
REGION_ALIAS("iram_seg", sram_seg);
|
||||
REGION_ALIAS("dram_seg", sram_seg);
|
||||
INCLUDE "targets/esp32-riscv.app.elf.ld";
|
||||
22
targets/esp32h2.memory.ld
Normal file
22
targets/esp32h2.memory.ld
Normal file
@@ -0,0 +1,22 @@
|
||||
# See "System Structure and Address Mapping" figure at
|
||||
# https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf
|
||||
|
||||
SRAM_START_ADDRESS = 0x40800000;
|
||||
SRAM_LEN = 0x50000;
|
||||
|
||||
CPU_FREQUENCY = 40000000;
|
||||
UART0_BAUD = 115200;
|
||||
|
||||
UART0_CLKDIV_REG = 0x60000014;
|
||||
UART0_CLKDIV_VAL = CPU_FREQUENCY / UART0_BAUD;
|
||||
UART0_STATUS = 0x6000001C;
|
||||
UART0_TX_ADDR = 0x60000000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
sram_seg (RWX) : org = SRAM_START_ADDRESS, len = SRAM_LEN
|
||||
}
|
||||
|
||||
REGION_ALIAS("iram_seg", sram_seg);
|
||||
REGION_ALIAS("dram_seg", sram_seg);
|
||||
INCLUDE "targets/esp32-riscv.app.elf.ld";
|
||||
21
targets/esp32h21.memory.ld
Normal file
21
targets/esp32h21.memory.ld
Normal file
@@ -0,0 +1,21 @@
|
||||
# Memory layout obtained from IDF linker files.
|
||||
|
||||
SRAM_START_ADDRESS = 0x40800000;
|
||||
SRAM_LEN = 0x50000;
|
||||
|
||||
CPU_FREQUENCY = 40000000;
|
||||
UART0_BAUD = 115200;
|
||||
|
||||
UART0_CLKDIV_REG = 0x60000014;
|
||||
UART0_CLKDIV_VAL = CPU_FREQUENCY / UART0_BAUD;
|
||||
UART0_STATUS = 0x6000001C;
|
||||
UART0_TX_ADDR = 0x60000000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
sram_seg (RWX) : org = SRAM_START_ADDRESS, len = SRAM_LEN
|
||||
}
|
||||
|
||||
REGION_ALIAS("iram_seg", sram_seg);
|
||||
REGION_ALIAS("dram_seg", sram_seg);
|
||||
INCLUDE "targets/esp32-riscv.app.elf.ld";
|
||||
23
targets/esp32p4.memory.ld
Normal file
23
targets/esp32p4.memory.ld
Normal file
@@ -0,0 +1,23 @@
|
||||
# Memory layout obtained from IDF linker files.
|
||||
|
||||
L2_CACHE_SIZE = 0x40000;
|
||||
|
||||
SRAM_START_ADDRESS = 0x4FF00000;
|
||||
SRAM_LEN = 0xC0000 - L2_CACHE_SIZE;
|
||||
|
||||
CPU_FREQUENCY = 40000000;
|
||||
UART0_BAUD = 115200;
|
||||
|
||||
UART0_CLKDIV_REG = 0x500CA014;
|
||||
UART0_CLKDIV_VAL = CPU_FREQUENCY / UART0_BAUD;
|
||||
UART0_STATUS = 0x500CA01C;
|
||||
UART0_TX_ADDR = 0x500CA000;
|
||||
|
||||
MEMORY
|
||||
{
|
||||
sram_seg (RWX) : org = SRAM_START_ADDRESS, len = SRAM_LEN
|
||||
}
|
||||
|
||||
REGION_ALIAS("iram_seg", sram_seg);
|
||||
REGION_ALIAS("dram_seg", sram_seg);
|
||||
INCLUDE "targets/esp32-riscv.app.elf.ld";
|
||||
@@ -2,15 +2,23 @@
|
||||
"llvm-target": "xtensa",
|
||||
"goos": "linux",
|
||||
"goarch": "arm",
|
||||
"build-tags": ["xtensa", "baremetal", "linux", "arm"],
|
||||
"build-tags": [
|
||||
"xtensa",
|
||||
"baremetal",
|
||||
"linux",
|
||||
"arm"
|
||||
],
|
||||
"gc": "conservative",
|
||||
"scheduler": "none",
|
||||
"cflags": [
|
||||
"-Werror",
|
||||
"-fshort-enums",
|
||||
"-Wno-macro-redefined",
|
||||
"-fno-exceptions", "-fno-unwind-tables", "-fno-asynchronous-unwind-tables",
|
||||
"-ffunction-sections", "-fdata-sections"
|
||||
"-fno-exceptions",
|
||||
"-fno-unwind-tables",
|
||||
"-fno-asynchronous-unwind-tables",
|
||||
"-ffunction-sections",
|
||||
"-fdata-sections"
|
||||
],
|
||||
"ldflags": [
|
||||
"--gc-sections"
|
||||
|
||||
@@ -49,6 +49,13 @@ func SplitPkgConfigFlags(s string) []string {
|
||||
for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
|
||||
// Check if next character is another flag (short flag with no argument)
|
||||
if i < len(s) && s[i] == '-' {
|
||||
// This is a short flag with no argument, finish current flag
|
||||
continue
|
||||
}
|
||||
|
||||
// Read content until next space
|
||||
for i < len(s) {
|
||||
if s[i] == '\\' && i+1 < len(s) && (s[i+1] == ' ' || s[i+1] == '\t') {
|
||||
|
||||
@@ -50,8 +50,8 @@ func TestSplitPkgConfigFlags(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("consecutive_flags", func(t *testing.T) {
|
||||
ftest("-I -L", `["-I-L"]`)
|
||||
ftest("-I -L /usr/lib", `["-I-L /usr/lib"]`)
|
||||
ftest("-I -L", `["-I" "-L"]`)
|
||||
ftest("-I -L /usr/lib", `["-I" "-L/usr/lib"]`)
|
||||
})
|
||||
|
||||
t.Run("edge_cases", func(t *testing.T) {
|
||||
@@ -59,7 +59,7 @@ func TestSplitPkgConfigFlags(t *testing.T) {
|
||||
ftest(" ", "[]")
|
||||
ftest("-", `["-"]`)
|
||||
ftest("-I", `["-I"]`)
|
||||
ftest("-I -", `["-I-"]`)
|
||||
ftest("-I -", `["-I" "-"]`)
|
||||
})
|
||||
|
||||
t.Run("escaped_spaces", func(t *testing.T) {
|
||||
@@ -77,6 +77,12 @@ func TestSplitPkgConfigFlags(t *testing.T) {
|
||||
ftest("-DVERSION=2.1 -DDEBUG=1", `["-DVERSION=2.1" "-DDEBUG=1"]`)
|
||||
ftest("-D VERSION=2.1 -D DEBUG=1", `["-DVERSION=2.1" "-DDEBUG=1"]`)
|
||||
})
|
||||
|
||||
// case for https://github.com/goplus/llgo/issues/1244
|
||||
t.Run("w_pipe", func(t *testing.T) {
|
||||
ftest("-w -pipe", `["-w" "-pipe"]`)
|
||||
ftest("-Os -w -pipe", `["-Os" "-w" "-pipe"]`)
|
||||
})
|
||||
}
|
||||
|
||||
func toString(ss []string) string {
|
||||
|
||||
Reference in New Issue
Block a user