Skip to content

Commit 254ee83

Browse files
committed
clang: init functions
1 parent c425abc commit 254ee83

17 files changed

Lines changed: 110 additions & 55 deletions

File tree

doc/spec.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,3 +1078,19 @@ There's no automatic order for declarations within a package. You need to declar
10781078

10791079
- If a function F uses a constant C or a variable V, you must declare V and C before F.
10801080
- If type B refers to type A, you must declare A before B.
1081+
1082+
### Init functions
1083+
1084+
Each package can have an `init()` function (with no arguments or return values) that runs automatically before `main()`. Unlike Go, only one `init` function is allowed per package.
1085+
1086+
Init functions can be used to initialize package-level variables with non-static values.
1087+
1088+
```go
1089+
var state int
1090+
1091+
func init() {
1092+
state = 42
1093+
}
1094+
```
1095+
1096+
If the program has multiple packages, each with its own `init` function, the order in which the `init` functions are called is not guaranteed.

internal/clang/emit.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ type Generator struct {
8282
symbols []symbol // pre-collected top-level declarations
8383
embeds Embeds // embedded C files from //so:embed
8484
comments ast.CommentMap // all comments across all files
85+
initFunc *ast.FuncDecl // package init() function, if any
8586
panicked bool // true after first panic caught in Visit
8687
}
8788

@@ -129,6 +130,7 @@ func (g *Generator) emitImpl(w io.Writer) {
129130
}
130131
ast.Walk(g, file)
131132
}
133+
g.emitInitFunc(w)
132134
}
133135

134136
// emitEmbeds writes the content of embedded files, separated by blank lines.
@@ -143,6 +145,27 @@ func (g *Generator) emitEmbeds(w io.Writer, files []embedFile) {
143145
}
144146
}
145147

148+
// emitInitFunc emits the package init() function as a GCC constructor
149+
// that runs automatically before main().
150+
func (g *Generator) emitInitFunc(w io.Writer) {
151+
if g.initFunc == nil {
152+
return
153+
}
154+
decl := g.initFunc
155+
g.state.funcSig = g.funcSig(decl)
156+
g.state.tempCount = 0
157+
158+
fmt.Fprintf(w, "\nstatic void __attribute__((constructor)) %s_init() {\n", g.pkg.Name)
159+
g.state.indent++
160+
g.walkStmts(decl.Body.List)
161+
g.emitDeferredCalls()
162+
g.state.indent--
163+
fmt.Fprintf(w, "}\n")
164+
165+
g.state.defers = nil
166+
g.state.funcSig = nil
167+
}
168+
146169
// collectComments builds a merged CommentMap from all source files.
147170
func (g *Generator) collectComments() {
148171
g.comments = ast.CommentMap{}

internal/clang/function.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ func (g *Generator) emitFuncDecl(decl *ast.FuncDecl) {
9696
if decl.Body == nil || g.hasExtern("", externFuncKey(decl)) {
9797
return
9898
}
99+
if decl.Name.Name == "init" {
100+
return
101+
}
99102
if decl.Recv != nil {
100103
g.emitMethodDecl(decl)
101104
return

internal/clang/symbols.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ func (g *Generator) collectSymbols() {
6969
if d.Body == nil || d.Name.Name == "main" {
7070
continue
7171
}
72+
if d.Name.Name == "init" {
73+
if g.initFunc != nil {
74+
g.fail(d.Name, "multiple init functions in package %s", g.pkg.Name)
75+
}
76+
g.initFunc = d
77+
continue
78+
}
7279
if g.hasExtern("", externFuncKey(d)) {
7380
continue
7481
}

so/os/extern.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ package os
66
//so:embed os.h
77
var os_h string
88

9-
//so:embed os.c
10-
var os_c string
11-
129
//so:extern
1310
var errno int
1411

@@ -27,6 +24,13 @@ const (
2724
//so:extern
2825
type os_file struct{}
2926

27+
//so:extern
28+
var (
29+
stdin *os_file
30+
stdout *os_file
31+
stderr *os_file
32+
)
33+
3034
// FILE *fopen(const char *restrict filename, const char *restrict mode);
3135
//
3236
//so:extern

so/os/file.go

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,10 @@ type File struct {
1010
closed bool
1111
}
1212

13-
// Stdin is the standard input file descriptor.
14-
//
15-
//so:extern
16-
var Stdin = File{}
17-
18-
// Stdout is the standard output file descriptor.
19-
//
20-
//so:extern
21-
var Stdout = File{}
22-
23-
// Stderr is the standard error file descriptor.
24-
//
25-
//so:extern
26-
var Stderr = File{}
13+
// Standard input, output, and error streams.
14+
var Stdin File
15+
var Stdout File
16+
var Stderr File
2717

2818
// Read reads up to len(b) bytes from the file and stores them in b.
2919
// It returns the number of bytes read and any error encountered.
@@ -135,3 +125,9 @@ func (f *File) Close() error {
135125
f.closed = true
136126
return nil
137127
}
128+
129+
func init() {
130+
Stdin = File{fd: stdin}
131+
Stdout = File{fd: stdout}
132+
Stderr = File{fd: stderr}
133+
}

so/os/os.c

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

so/os/os.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ typedef struct {
1010
bool closed;
1111
} os_File;
1212

13-
// Stdin, Stdout and Stderr are the standard
14-
// input, output and error file descriptors.
15-
extern os_File os_Stdin;
16-
extern os_File os_Stdout;
17-
extern os_File os_Stderr;
18-
1913
// Error codes.
2014
#define os_EACCES EACCES
2115
#define os_EEXIST EEXIST

so/time/arithm.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ func subMono(t, u int64) Duration {
358358
func Since(t Time) Duration {
359359
if (t.wall & hasMonotonic) != 0 {
360360
// Common case optimization: if t has monotonic time, then Sub will use only it.
361-
return subMono(time_mono()-time_monoStart, t.ext)
361+
return subMono(time_mono()-monoStart, t.ext)
362362
}
363363
return Now().Sub(t)
364364
}
@@ -368,7 +368,7 @@ func Since(t Time) Duration {
368368
func Until(t Time) Duration {
369369
if (t.wall & hasMonotonic) != 0 {
370370
// Common case optimization: if t has monotonic time, then Sub will use only it.
371-
return subMono(t.ext, time_mono()-time_monoStart)
371+
return subMono(t.ext, time_mono()-monoStart)
372372
}
373373
return t.Sub(Now())
374374
}

so/time/extern.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ import _ "embed"
55
//so:embed time.h
66
var time_h string
77

8-
//so:embed time.c
9-
var time_c string
10-
118
//so:extern
129
type time_tm struct {
1310
tm_sec int
@@ -53,6 +50,8 @@ func time_mono() int64 { return 0 }
5350
// which appears to have a default resolution of 15ms),
5451
// we avoid ever reporting a monotonic time of 0.
5552
// (Callers may want to use 0 as "time not set".)
56-
//
57-
//so:extern
58-
var time_monoStart int64 = time_mono() - 1
53+
var monoStart int64
54+
55+
func init() {
56+
monoStart = time_mono() - 1
57+
}

0 commit comments

Comments
 (0)