rewrite: address review feedback

This commit is contained in:
Li Jie
2025-11-14 17:57:05 +08:00
parent 2a52d422c5
commit 3a1d8693e9
4 changed files with 103 additions and 49 deletions

View File

@@ -130,12 +130,14 @@ type context struct {
rewrites map[string]string
}
const maxStringTypeDepth = 64
func (p *context) rewriteValue(name string) (string, bool) {
if p.rewrites == nil {
return "", false
}
dot := strings.LastIndex(name, ".")
if dot < 0 {
if dot < 0 || dot == len(name)-1 {
return "", false
}
varName := name[dot+1:]
@@ -144,7 +146,9 @@ func (p *context) rewriteValue(name string) (string, bool) {
}
func (p *context) isStringType(typ types.Type) bool {
for typ != nil {
depth := 0
for typ != nil && depth < maxStringTypeDepth {
depth++
switch t := typ.Underlying().(type) {
case *types.Basic:
return t.Kind() == types.String
@@ -164,15 +168,21 @@ func (p *context) globalFullName(g *ssa.Global) string {
}
func (p *context) rewriteInitStore(store *ssa.Store, g *ssa.Global) (string, bool) {
if p.rewrites == nil {
return "", false
}
fn := store.Block().Parent()
if fn == nil || fn.Synthetic != "package initializer" {
return "", false
}
value, ok := p.rewriteValue(p.globalFullName(g))
if !ok || !p.isStringType(g.Type()) {
if _, ok := store.Val.(*ssa.Const); !ok {
return "", false
}
if _, ok := store.Val.(*ssa.Const); !ok {
if !p.isStringType(g.Type()) {
return "", false
}
value, ok := p.rewriteValue(p.globalFullName(g))
if !ok {
return "", false
}
return value, true
@@ -867,9 +877,11 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
return
}
}
if g, ok := va.(*ssa.Global); ok {
if _, ok := p.rewriteInitStore(v, g); ok {
return
if p.rewrites != nil {
if g, ok := va.(*ssa.Global); ok {
if _, ok := p.rewriteInitStore(v, g); ok {
return
}
}
}
ptr := p.compileValue(b, va)
@@ -1041,6 +1053,16 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
}
// NewPackageEx compiles a Go package to LLVM IR package.
//
// Parameters:
// - prog: target LLVM SSA program context
// - patches: optional package patches applied during compilation
// - rewrites: per-package string initializers rewritten at compile time
// - pkg: SSA package to compile
// - files: parsed AST files that belong to the package
//
// The rewrites map uses short variable names (without package qualifier) and
// only affects string-typed globals defined in the current package.
func NewPackageEx(prog llssa.Program, patches Patches, rewrites map[string]string, pkg *ssa.Package, files []*ast.File) (ret llssa.Package, externs []string, err error) {
pkgProg := pkg.Prog
pkgTypes := pkg.Pkg

View File

@@ -90,6 +90,9 @@ func TestRewriteValueNoDot(t *testing.T) {
if _, ok := ctx.rewriteValue("VarInit"); ok {
t.Fatalf("rewriteValue should skip names without package prefix")
}
if _, ok := ctx.rewriteValue("pkg."); ok {
t.Fatalf("rewriteValue should skip trailing dot names")
}
}
func TestIsStringTypeDefault(t *testing.T) {

View File

@@ -111,28 +111,33 @@ type OutFmtDetails struct {
}
type Config struct {
Goos string
Goarch string
Target string // target name (e.g., "rp2040", "wasi") - takes precedence over Goos/Goarch
BinPath string
AppExt string // ".exe" on Windows, empty on Unix
OutFile string // only valid for ModeBuild when len(pkgs) == 1
OutFmts OutFmts // Output format specifications (only for Target != "")
Emulator bool // run in emulator mode
Port string // target port for flashing
BaudRate int // baudrate for serial communication
RunArgs []string
Mode Mode
BuildMode BuildMode // Build mode: exe, c-archive, c-shared
AbiMode AbiMode
GenExpect bool // only valid for ModeCmpTest
Verbose bool
GenLL bool // generate pkg .ll files
CheckLLFiles bool // check .ll files valid
CheckLinkArgs bool // check linkargs valid
ForceEspClang bool // force to use esp-clang
Tags string
GlobalRewrites map[string]Rewrites // pkg => var => value
Goos string
Goarch string
Target string // target name (e.g., "rp2040", "wasi") - takes precedence over Goos/Goarch
BinPath string
AppExt string // ".exe" on Windows, empty on Unix
OutFile string // only valid for ModeBuild when len(pkgs) == 1
OutFmts OutFmts // Output format specifications (only for Target != "")
Emulator bool // run in emulator mode
Port string // target port for flashing
BaudRate int // baudrate for serial communication
RunArgs []string
Mode Mode
BuildMode BuildMode // Build mode: exe, c-archive, c-shared
AbiMode AbiMode
GenExpect bool // only valid for ModeCmpTest
Verbose bool
GenLL bool // generate pkg .ll files
CheckLLFiles bool // check .ll files valid
CheckLinkArgs bool // check linkargs valid
ForceEspClang bool // force to use esp-clang
Tags string
// GlobalRewrites specifies compile-time overrides for global string variables.
// Keys are fully qualified package paths (e.g. "main" or "github.com/user/pkg").
// Each Rewrites entry maps variable names to replacement string values. Only
// string-typed globals are supported and "main" applies to all root main
// packages in the current build.
GlobalRewrites map[string]Rewrites
}
type Rewrites map[string]string
@@ -626,6 +631,8 @@ var (
errXflags = errors.New("-X flag requires argument of the form importpath.name=value")
)
const maxRewriteValueLength = 1 << 20 // 1 MiB cap per rewrite value
func addGlobalString(conf *Config, arg string, mainPkgs []string) {
addGlobalStringWith(conf, arg, mainPkgs, true)
}
@@ -637,6 +644,9 @@ func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExis
panic(errXflags)
}
pkg := arg[:dot]
varName := arg[dot+1 : eq]
value := arg[eq+1:]
validateRewriteInput(pkg, varName, value)
pkgs := []string{pkg}
if pkg == "main" {
pkgs = mainPkgs
@@ -647,8 +657,6 @@ func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExis
if conf.GlobalRewrites == nil {
conf.GlobalRewrites = make(map[string]Rewrites)
}
varName := arg[dot+1 : eq]
value := arg[eq+1:]
for _, realPkg := range pkgs {
vars := conf.GlobalRewrites[realPkg]
if vars == nil {
@@ -664,6 +672,18 @@ func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExis
}
}
func validateRewriteInput(pkg, varName, value string) {
if pkg == "" || strings.ContainsAny(pkg, " \t\r\n") {
panic(fmt.Errorf("invalid package path for rewrite: %q", pkg))
}
if !token.IsIdentifier(varName) {
panic(fmt.Errorf("invalid variable name for rewrite: %q", varName))
}
if len(value) > maxRewriteValueLength {
panic(fmt.Errorf("rewrite value too large: %d bytes", len(value)))
}
}
// compileExtraFiles compiles extra files (.s/.c) from target configuration and returns object files
func compileExtraFiles(ctx *context, verbose bool) ([]string, error) {
if len(ctx.crossCompile.ExtraFiles) == 0 {
@@ -1182,23 +1202,29 @@ func allPkgs(ctx *context, initial []*packages.Package, verbose bool) (all []*aP
}
func collectRewriteVars(ctx *context, pkgPath string) map[string]string {
var rewrites map[string]string
basePath := strings.TrimPrefix(pkgPath, altPkgPathPrefix)
if data := ctx.buildConf.GlobalRewrites; len(data) != 0 {
for pkg, vars := range data {
trimmed := strings.TrimPrefix(pkg, altPkgPathPrefix)
if trimmed != basePath {
continue
}
for name, value := range vars {
if rewrites == nil {
rewrites = make(map[string]string)
}
rewrites[name] = value
}
}
data := ctx.buildConf.GlobalRewrites
if len(data) == 0 {
return nil
}
return rewrites
basePath := strings.TrimPrefix(pkgPath, altPkgPathPrefix)
if vars := data[basePath]; vars != nil {
return cloneRewrites(vars)
}
if vars := data[pkgPath]; vars != nil {
return cloneRewrites(vars)
}
return nil
}
func cloneRewrites(src Rewrites) map[string]string {
if len(src) == 0 {
return nil
}
dup := make(map[string]string, len(src))
for k, v := range src {
dup[k] = v
}
return dup
}
func createSSAPkg(prog *ssa.Program, p *packages.Package, verbose bool) *ssa.Package {

View File

@@ -31,7 +31,10 @@ func (pkg Package) AddGlobalString(name string, value string) {
pkg.NewVarEx(name, prog.Pointer(styp)).Init(Expr{cv, styp})
}
// ConstString returns an SSA string constant expression within this package.
// ConstString creates an SSA expression representing a Go string literal. The
// returned value is backed by an anonymous global constant and can be used to
// initialize package-level variables or other constant contexts that expect a
// Go string value.
func (pkg Package) ConstString(value string) Expr {
prog := pkg.Prog
styp := prog.String()