rewrite: address review feedback
This commit is contained in:
@@ -130,12 +130,14 @@ type context struct {
|
|||||||
rewrites map[string]string
|
rewrites map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxStringTypeDepth = 64
|
||||||
|
|
||||||
func (p *context) rewriteValue(name string) (string, bool) {
|
func (p *context) rewriteValue(name string) (string, bool) {
|
||||||
if p.rewrites == nil {
|
if p.rewrites == nil {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
dot := strings.LastIndex(name, ".")
|
dot := strings.LastIndex(name, ".")
|
||||||
if dot < 0 {
|
if dot < 0 || dot == len(name)-1 {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
varName := name[dot+1:]
|
varName := name[dot+1:]
|
||||||
@@ -144,7 +146,9 @@ func (p *context) rewriteValue(name string) (string, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *context) isStringType(typ types.Type) 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) {
|
switch t := typ.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
return t.Kind() == types.String
|
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) {
|
func (p *context) rewriteInitStore(store *ssa.Store, g *ssa.Global) (string, bool) {
|
||||||
|
if p.rewrites == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
fn := store.Block().Parent()
|
fn := store.Block().Parent()
|
||||||
if fn == nil || fn.Synthetic != "package initializer" {
|
if fn == nil || fn.Synthetic != "package initializer" {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
value, ok := p.rewriteValue(p.globalFullName(g))
|
if _, ok := store.Val.(*ssa.Const); !ok {
|
||||||
if !ok || !p.isStringType(g.Type()) {
|
|
||||||
return "", false
|
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 "", false
|
||||||
}
|
}
|
||||||
return value, true
|
return value, true
|
||||||
@@ -867,9 +877,11 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if g, ok := va.(*ssa.Global); ok {
|
if p.rewrites != nil {
|
||||||
if _, ok := p.rewriteInitStore(v, g); ok {
|
if g, ok := va.(*ssa.Global); ok {
|
||||||
return
|
if _, ok := p.rewriteInitStore(v, g); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ptr := p.compileValue(b, va)
|
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.
|
// 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) {
|
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
|
pkgProg := pkg.Prog
|
||||||
pkgTypes := pkg.Pkg
|
pkgTypes := pkg.Pkg
|
||||||
|
|||||||
@@ -90,6 +90,9 @@ func TestRewriteValueNoDot(t *testing.T) {
|
|||||||
if _, ok := ctx.rewriteValue("VarInit"); ok {
|
if _, ok := ctx.rewriteValue("VarInit"); ok {
|
||||||
t.Fatalf("rewriteValue should skip names without package prefix")
|
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) {
|
func TestIsStringTypeDefault(t *testing.T) {
|
||||||
|
|||||||
@@ -111,28 +111,33 @@ type OutFmtDetails struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Goos string
|
Goos string
|
||||||
Goarch string
|
Goarch string
|
||||||
Target string // target name (e.g., "rp2040", "wasi") - takes precedence over Goos/Goarch
|
Target string // target name (e.g., "rp2040", "wasi") - takes precedence over Goos/Goarch
|
||||||
BinPath string
|
BinPath string
|
||||||
AppExt string // ".exe" on Windows, empty on Unix
|
AppExt string // ".exe" on Windows, empty on Unix
|
||||||
OutFile string // only valid for ModeBuild when len(pkgs) == 1
|
OutFile string // only valid for ModeBuild when len(pkgs) == 1
|
||||||
OutFmts OutFmts // Output format specifications (only for Target != "")
|
OutFmts OutFmts // Output format specifications (only for Target != "")
|
||||||
Emulator bool // run in emulator mode
|
Emulator bool // run in emulator mode
|
||||||
Port string // target port for flashing
|
Port string // target port for flashing
|
||||||
BaudRate int // baudrate for serial communication
|
BaudRate int // baudrate for serial communication
|
||||||
RunArgs []string
|
RunArgs []string
|
||||||
Mode Mode
|
Mode Mode
|
||||||
BuildMode BuildMode // Build mode: exe, c-archive, c-shared
|
BuildMode BuildMode // Build mode: exe, c-archive, c-shared
|
||||||
AbiMode AbiMode
|
AbiMode AbiMode
|
||||||
GenExpect bool // only valid for ModeCmpTest
|
GenExpect bool // only valid for ModeCmpTest
|
||||||
Verbose bool
|
Verbose bool
|
||||||
GenLL bool // generate pkg .ll files
|
GenLL bool // generate pkg .ll files
|
||||||
CheckLLFiles bool // check .ll files valid
|
CheckLLFiles bool // check .ll files valid
|
||||||
CheckLinkArgs bool // check linkargs valid
|
CheckLinkArgs bool // check linkargs valid
|
||||||
ForceEspClang bool // force to use esp-clang
|
ForceEspClang bool // force to use esp-clang
|
||||||
Tags string
|
Tags string
|
||||||
GlobalRewrites map[string]Rewrites // pkg => var => value
|
// 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
|
type Rewrites map[string]string
|
||||||
@@ -626,6 +631,8 @@ var (
|
|||||||
errXflags = errors.New("-X flag requires argument of the form importpath.name=value")
|
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) {
|
func addGlobalString(conf *Config, arg string, mainPkgs []string) {
|
||||||
addGlobalStringWith(conf, arg, mainPkgs, true)
|
addGlobalStringWith(conf, arg, mainPkgs, true)
|
||||||
}
|
}
|
||||||
@@ -637,6 +644,9 @@ func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExis
|
|||||||
panic(errXflags)
|
panic(errXflags)
|
||||||
}
|
}
|
||||||
pkg := arg[:dot]
|
pkg := arg[:dot]
|
||||||
|
varName := arg[dot+1 : eq]
|
||||||
|
value := arg[eq+1:]
|
||||||
|
validateRewriteInput(pkg, varName, value)
|
||||||
pkgs := []string{pkg}
|
pkgs := []string{pkg}
|
||||||
if pkg == "main" {
|
if pkg == "main" {
|
||||||
pkgs = mainPkgs
|
pkgs = mainPkgs
|
||||||
@@ -647,8 +657,6 @@ func addGlobalStringWith(conf *Config, arg string, mainPkgs []string, skipIfExis
|
|||||||
if conf.GlobalRewrites == nil {
|
if conf.GlobalRewrites == nil {
|
||||||
conf.GlobalRewrites = make(map[string]Rewrites)
|
conf.GlobalRewrites = make(map[string]Rewrites)
|
||||||
}
|
}
|
||||||
varName := arg[dot+1 : eq]
|
|
||||||
value := arg[eq+1:]
|
|
||||||
for _, realPkg := range pkgs {
|
for _, realPkg := range pkgs {
|
||||||
vars := conf.GlobalRewrites[realPkg]
|
vars := conf.GlobalRewrites[realPkg]
|
||||||
if vars == nil {
|
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
|
// compileExtraFiles compiles extra files (.s/.c) from target configuration and returns object files
|
||||||
func compileExtraFiles(ctx *context, verbose bool) ([]string, error) {
|
func compileExtraFiles(ctx *context, verbose bool) ([]string, error) {
|
||||||
if len(ctx.crossCompile.ExtraFiles) == 0 {
|
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 {
|
func collectRewriteVars(ctx *context, pkgPath string) map[string]string {
|
||||||
var rewrites map[string]string
|
data := ctx.buildConf.GlobalRewrites
|
||||||
basePath := strings.TrimPrefix(pkgPath, altPkgPathPrefix)
|
if len(data) == 0 {
|
||||||
if data := ctx.buildConf.GlobalRewrites; len(data) != 0 {
|
return nil
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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 {
|
func createSSAPkg(prog *ssa.Program, p *packages.Package, verbose bool) *ssa.Package {
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ func (pkg Package) AddGlobalString(name string, value string) {
|
|||||||
pkg.NewVarEx(name, prog.Pointer(styp)).Init(Expr{cv, styp})
|
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 {
|
func (pkg Package) ConstString(value string) Expr {
|
||||||
prog := pkg.Prog
|
prog := pkg.Prog
|
||||||
styp := prog.String()
|
styp := prog.String()
|
||||||
|
|||||||
Reference in New Issue
Block a user