commit b2c078511489aa871aed0d68e564019ee8c02760
parent d1144788d5d7e4b65ab69dff577f284420eefc8c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 25 May 2026 17:12:02 -0700
Add split section codegen flags
Diffstat:
7 files changed, 112 insertions(+), 13 deletions(-)
diff --git a/Makefile b/Makefile
@@ -444,8 +444,19 @@ BOOTSTRAP_STAGE1_BIN = $(BOOTSTRAP_STAGE1_DIR)/cfree
BOOTSTRAP_STAGE2_BIN = $(BOOTSTRAP_STAGE2_DIR)/cfree
BOOTSTRAP_STAGE3_BIN = $(BOOTSTRAP_STAGE3_DIR)/cfree
BOOTSTRAP_TOOLS = cc ld ar ranlib as
+BOOTSTRAP_HOST_MODE_CFLAGS =
+BOOTSTRAP_STAMP = $(BOOTSTRAP_DIR)/.stamp
+BOOTSTRAP_RT_LIBS = $(addprefix $(RT_BUILD_DIR)/,$(addsuffix /libcfree_rt.a,$(RT_DEFAULT_VARIANTS)))
+BOOTSTRAP_MAKEFILES = Makefile mk/config.mk rt/Makefile
-bootstrap: $(BIN) rt
+ifeq ($(RELEASE),1)
+bootstrap $(BOOTSTRAP_STAMP): HOST_OPTFLAGS = -O1
+bootstrap $(BOOTSTRAP_STAMP): BOOTSTRAP_HOST_MODE_CFLAGS = $(HOST_MODE_CFLAGS)
+endif
+
+bootstrap: $(BOOTSTRAP_STAMP)
+
+$(BOOTSTRAP_STAMP): $(BIN) $(BOOTSTRAP_RT_LIBS) $(BOOTSTRAP_MAKEFILES)
rm -rf $(BOOTSTRAP_DIR)
@mkdir -p $(BOOTSTRAP_STAGE1_DIR)
cp $(BIN) $(BOOTSTRAP_STAGE1_BIN)
@@ -454,7 +465,7 @@ bootstrap: $(BIN) rt
BUILD_DIR='$(abspath $(BOOTSTRAP_STAGE2_DIR))' \
RELEASE='$(RELEASE)' \
HOST_OPTFLAGS='$(HOST_OPTFLAGS)' \
- HOST_MODE_CFLAGS= \
+ HOST_MODE_CFLAGS='$(BOOTSTRAP_HOST_MODE_CFLAGS)' \
HOST_MODE_LDFLAGS= \
CC='$(abspath $(BOOTSTRAP_STAGE1_DIR))/cc' \
AR='$(abspath $(BOOTSTRAP_STAGE1_DIR))/ar' \
@@ -464,15 +475,16 @@ bootstrap: $(BIN) rt
BUILD_DIR='$(abspath $(BOOTSTRAP_STAGE3_DIR))' \
RELEASE='$(RELEASE)' \
HOST_OPTFLAGS='$(HOST_OPTFLAGS)' \
- HOST_MODE_CFLAGS= \
+ HOST_MODE_CFLAGS='$(BOOTSTRAP_HOST_MODE_CFLAGS)' \
HOST_MODE_LDFLAGS= \
CC='$(abspath $(BOOTSTRAP_STAGE2_DIR))/cc' \
AR='$(abspath $(BOOTSTRAP_STAGE2_DIR))/ar' \
LD='$(abspath $(BOOTSTRAP_STAGE2_DIR))/ld'
cmp $(BOOTSTRAP_STAGE2_BIN) $(BOOTSTRAP_STAGE3_BIN)
shasum -a 256 $(BOOTSTRAP_STAGE2_BIN) $(BOOTSTRAP_STAGE3_BIN)
+ @touch $@
-bootstrap-test-toy: bootstrap
+bootstrap-test-toy: $(BOOTSTRAP_STAMP)
@CFREE='$(abspath $(BOOTSTRAP_STAGE3_BIN))' test/toy/run.sh
bench-opt: bin
diff --git a/driver/cc.c b/driver/cc.c
@@ -119,6 +119,8 @@ typedef struct CcOptions {
int emit_asm_source; /* -S */
int opt_level; /* -O0/-O1/-O2 */
int debug_info; /* -g */
+ int function_sections; /* -ffunction-sections */
+ int data_sections; /* -fdata-sections */
int warnings_are_errors; /* -Werror */
uint32_t max_errors; /* -fmax-errors=N */
CfreeTarget target; /* -target / host */
@@ -1172,6 +1174,22 @@ static int cc_parse(int argc, char** argv, CcOptions* o) {
o->freestanding = 0;
continue;
}
+ if (driver_streq(a, "-ffunction-sections")) {
+ o->function_sections = 1;
+ continue;
+ }
+ if (driver_streq(a, "-fno-function-sections")) {
+ o->function_sections = 0;
+ continue;
+ }
+ if (driver_streq(a, "-fdata-sections")) {
+ o->data_sections = 1;
+ continue;
+ }
+ if (driver_streq(a, "-fno-data-sections")) {
+ o->data_sections = 0;
+ continue;
+ }
if (driver_streq(a, "-nostdinc")) {
o->nostdinc = 1;
continue;
@@ -2113,6 +2131,8 @@ static void cc_fill_c_opts(const CcOptions* o, const CfreePreprocessOptions* pp,
copts->code.default_visibility = o->default_visibility;
copts->code.emit_c_source = o->emit_c_source ? true : false;
copts->code.emit_asm_source = o->emit_asm_source ? true : false;
+ copts->code.function_sections = o->function_sections ? true : false;
+ copts->code.data_sections = o->data_sections ? true : false;
copts->code.epoch = o->epoch;
copts->code.path_map = o->npath_map ? o->path_map : NULL;
copts->code.npath_map = o->npath_map;
diff --git a/include/cfree/core.h b/include/cfree/core.h
@@ -216,6 +216,10 @@ typedef struct CfreeCodeOptions {
* machine-code path runs, and the assembled bytes are disassembled into
* GAS-syntax text after compilation. Implies compile_only / no link. */
bool emit_asm_source;
+ /* Place each defined function/object with a linker-visible symbol in a
+ * per-symbol section when no explicit frontend section was requested. */
+ bool function_sections;
+ bool data_sections;
uint64_t epoch; /* reproducible timestamp seed; 0 means no timestamp */
const CfreePathPrefixMap* path_map;
uint32_t npath_map;
diff --git a/src/cg/data.c b/src/cg/data.c
@@ -66,6 +66,7 @@ void cfree_cg_data_begin(CfreeCg* g, CfreeCgSym cg_sym,
SecKind sec_kind;
u16 sec_flags;
Sym sec_name_sym;
+ Slice split_base;
ObjSecId sec;
CfreeCgDecl decl_attrs;
if (!g) return;
@@ -114,17 +115,26 @@ void cfree_cg_data_begin(CfreeCg* g, CfreeCgSym cg_sym,
if (!attrs.section && decl_attrs.as.object.section) {
attrs.section = decl_attrs.as.object.section;
}
+ split_base = SLICE_NULL;
if ((decl_attrs.as.object.flags & CFREE_CG_OBJ_TLS) &&
(attrs.flags & CFREE_CG_DATADEF_ZERO_FILL)) {
sec_kind = SEC_BSS;
sec_flags = SF_ALLOC | SF_WRITE | SF_TLS;
- sec_name_sym = attrs.section ? (Sym)attrs.section : obj_secname_tbss(c);
+ if (attrs.section) {
+ sec_name_sym = (Sym)attrs.section;
+ } else {
+ sec_name_sym = obj_secname_tbss(c);
+ split_base = pool_slice(c->global, sec_name_sym);
+ }
} else if (attrs.flags & CFREE_CG_DATADEF_ZERO_FILL) {
sec_kind = SEC_BSS;
sec_flags = SF_ALLOC | SF_WRITE;
- sec_name_sym = attrs.section
- ? (Sym)attrs.section
- : pool_intern_slice(c->global, SLICE_LIT(".bss"));
+ if (attrs.section) {
+ sec_name_sym = (Sym)attrs.section;
+ } else {
+ split_base = SLICE_LIT(".bss");
+ sec_name_sym = pool_intern_slice(c->global, split_base);
+ }
} else if (attrs.section) {
sec_name_sym = (Sym)attrs.section;
if (attrs.flags & CFREE_CG_DATADEF_READONLY) {
@@ -138,15 +148,23 @@ void cfree_cg_data_begin(CfreeCg* g, CfreeCgSym cg_sym,
(decl_attrs.as.object.flags & CFREE_CG_OBJ_READONLY)) {
sec_kind = SEC_RODATA;
sec_flags = SF_ALLOC;
- sec_name_sym = pool_intern_slice(c->global, SLICE_LIT(".rodata"));
+ split_base = SLICE_LIT(".rodata");
+ sec_name_sym = pool_intern_slice(c->global, split_base);
} else if (decl_attrs.as.object.flags & CFREE_CG_OBJ_TLS) {
sec_kind = SEC_DATA;
sec_flags = SF_ALLOC | SF_WRITE | SF_TLS;
sec_name_sym = obj_secname_tdata(c);
+ split_base = pool_slice(c->global, sec_name_sym);
} else {
sec_kind = SEC_DATA;
sec_flags = SF_ALLOC | SF_WRITE;
- sec_name_sym = pool_intern_slice(c->global, SLICE_LIT(".data"));
+ split_base = SLICE_LIT(".data");
+ sec_name_sym = pool_intern_slice(c->global, split_base);
+ }
+ if (!attrs.section && g->data_sections && split_base.len) {
+ Sym split_name =
+ api_cg_symbol_section_name(g, split_base, decl_attrs.linkage_name);
+ if (split_name) sec_name_sym = split_name;
}
if (attrs.flags & CFREE_CG_DATADEF_RETAIN) sec_flags |= SF_RETAIN;
if (attrs.flags & CFREE_CG_DATADEF_MERGE) sec_flags |= SF_MERGE;
diff --git a/src/cg/internal.h b/src/cg/internal.h
@@ -158,6 +158,9 @@ struct CfreeCg {
u32 rodata_counter;
int opt_level;
u8 check_only;
+ u8 function_sections;
+ u8 data_sections;
+ u8 section_pad[1];
ObjSecId data_sec;
ObjSymId data_sym;
@@ -366,6 +369,7 @@ void cfree_cg_func_end(CfreeCg* g);
SymBind api_map_bind(CfreeSymBind b);
SymVis api_map_vis(CfreeCgVisibility v);
SymKind api_decl_sym_kind(CfreeCgDecl decl);
+Sym api_cg_symbol_section_name(CfreeCg* g, Slice base, CfreeSym linkage_name);
void api_remember_sym(CfreeCg* g, ObjSymId sym, CfreeCgTypeId ty,
CfreeCgDecl decl);
CfreeCgTypeId api_sym_type(CfreeCg* g, CfreeCgSym sym);
diff --git a/src/cg/session.c b/src/cg/session.c
@@ -61,6 +61,8 @@ static void cg_free_obj_state(CfreeCg* g) {
g->nscopes = 0;
g->scope_generation = 0;
g->rodata_counter = 0;
+ g->function_sections = 0;
+ g->data_sections = 0;
g->data_sec = OBJ_SEC_NONE;
g->data_sym = OBJ_SYM_NONE;
g->data_base = 0;
@@ -125,6 +127,8 @@ CfreeStatus cfree_cg_begin_obj(CfreeCg* g, CfreeObjBuilder* out,
g->debug = target->debug;
g->opt_level = opt_level;
g->check_only = (opts && opts->check_only) ? 1u : 0u;
+ g->function_sections = (opts && opts->function_sections) ? 1u : 0u;
+ g->data_sections = (opts && opts->data_sections) ? 1u : 0u;
return CFREE_OK;
}
@@ -250,6 +254,7 @@ void cfree_cg_func_begin_attrs(CfreeCg* g, CfreeCgSym cg_sym,
CfreeCgTypeId fty;
const ABIFuncInfo* abi;
CfreeCgDecl attrs;
+ Sym sec_name;
if (!g) return;
c = g->c;
ob = g->obj;
@@ -260,9 +265,14 @@ void cfree_cg_func_begin_attrs(CfreeCg* g, CfreeCgSym cg_sym,
attrs = api_sym_attrs(g, cg_sym);
abi = abi_cg_func_info(c->abi, fty);
- text_sec = obj_section(ob, pool_intern_slice(c->global, SLICE_LIT(".text")),
- SEC_TEXT,
- SF_EXEC | SF_ALLOC, 4);
+ sec_name = begin_attrs.section ? (Sym)begin_attrs.section
+ : (Sym)attrs.as.func.section;
+ if (!sec_name && g->function_sections) {
+ sec_name =
+ api_cg_symbol_section_name(g, SLICE_LIT(".text"), attrs.linkage_name);
+ }
+ if (!sec_name) sec_name = pool_intern_slice(c->global, SLICE_LIT(".text"));
+ text_sec = obj_section(ob, sec_name, SEC_TEXT, SF_EXEC | SF_ALLOC, 4);
if (sym != OBJ_SYM_NONE) {
obj_symbol_define(ob, sym, text_sec, 0, 0);
diff --git a/src/cg/symbol.c b/src/cg/symbol.c
@@ -33,6 +33,37 @@ SymKind api_decl_sym_kind(CfreeCgDecl decl) {
return SK_OBJ;
}
+Sym api_cg_symbol_section_name(CfreeCg* g, Slice base, CfreeSym linkage_name) {
+ Compiler* c;
+ Heap* h;
+ Slice name;
+ size_t len;
+ char* buf;
+ Sym out;
+ if (!g || !base.len || !linkage_name) return 0;
+ c = g->c;
+ name = pool_slice(c->global, (Sym)linkage_name);
+ if (!name.len) return 0;
+ if (base.len > SIZE_MAX - 2u || name.len > SIZE_MAX - base.len - 2u) {
+ compiler_panic(c, api_no_loc(), "CfreeCg: section name too long");
+ return 0;
+ }
+ len = base.len + 1u + name.len;
+ h = c->ctx->heap;
+ buf = (char*)h->alloc(h, len + 1u, 1);
+ if (!buf) {
+ compiler_panic(c, api_no_loc(), "CfreeCg: oom building section name");
+ return 0;
+ }
+ memcpy(buf, base.s, base.len);
+ buf[base.len] = '.';
+ memcpy(buf + base.len + 1u, name.s, name.len);
+ buf[len] = '\0';
+ out = pool_intern_slice(c->global, (Slice){.s = buf, .len = len});
+ h->free(h, buf, len + 1u);
+ return out;
+}
+
void api_remember_sym(CfreeCg* g, ObjSymId sym, CfreeCgTypeId ty,
CfreeCgDecl decl) {
Heap* h;