mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 00:44:27 +00:00
Warnings for big copies: a struct with 256+ bytes of plain data as a parameter or in for x in list
- on stderr (finch ir stays the IR), as yellow warnings in editors (@W in the index), once per place - str, arrays and maps inside a struct don't count: passing it copies only their handles - none in the compiler, the examples or the tests other than the new one - docs: the warning instead of "not planned"; what was and wasn't checked for Android Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GzNP6r8dg6WDxUVZy8wjL
This commit is contained in:
parent
961ba951ec
commit
d557b5505e
15 changed files with 210 additions and 18 deletions
66
src/gen.fch
66
src/gen.fch
|
|
@ -7,6 +7,7 @@ import ast
|
|||
import docs
|
||||
import cimport
|
||||
import platform
|
||||
import "stdio.h"
|
||||
|
||||
struct Var {
|
||||
str name
|
||||
|
|
@ -166,6 +167,7 @@ struct Ref {
|
|||
|
||||
struct Gen {
|
||||
[]ast.Program progs
|
||||
[]str warned // file:line:col of the warnings given
|
||||
bool trace // each function keeps a frame { prev, name, file, line } in @finch_trace
|
||||
str frame // the current function's frame ("" in helpers)
|
||||
str frameLine // where its line goes
|
||||
|
|
@ -337,6 +339,58 @@ fn fail(ptr[Gen] g, ast.Node n, str msg) {
|
|||
exit(1)
|
||||
}
|
||||
|
||||
// A warning: the program is still built. On stderr (finch ir's output stays the IR); an @W record for editors.
|
||||
fn warn(ptr[Gen] g, ast.Node n, str msg) {
|
||||
where := g.files[n.file] + ":" + str(n.line) + ":" + str(n.col)
|
||||
if g.warned.contains(where) {
|
||||
return // once per place (a generic function is built more than once)
|
||||
}
|
||||
g.warned.push(where)
|
||||
if g.index {
|
||||
g.idx.push("@W\t" + g.files[n.file] + "\t" + str(n.line) + "\t" + str(n.col) + "\t" + msg)
|
||||
return
|
||||
}
|
||||
fputs(where + ": warning: " + msg + "\n", stderr)
|
||||
}
|
||||
|
||||
// About how many bytes of plain data (numbers, structs of them, C's [N]T) a value has, for the copy warnings
|
||||
fn byteSize(ptr[Gen] g, ast.Type t) -> int {
|
||||
k := t.kind
|
||||
if k == "struct" {
|
||||
sd := g.structs[findStruct(g, t.module, t.name)]
|
||||
if sd.isC && sd.csize > 0 {
|
||||
return sd.csize
|
||||
}
|
||||
n := 0
|
||||
for ft in sd.ftypes {
|
||||
n += byteSize(g, ft)
|
||||
}
|
||||
return n
|
||||
}
|
||||
if k == "fixed" && t.elem.len > 0 {
|
||||
return t.count * byteSize(g, t.elem[0])
|
||||
}
|
||||
if k == "str" || k == "array" || k == "map" {
|
||||
return 0 // only its { ptr, len, cap } is copied when it is passed: not counted
|
||||
}
|
||||
if k == "ptr" || k == "enum" || k == "int" || k == "float" {
|
||||
return 8
|
||||
}
|
||||
return bitsOf(t) / 8 + int(bitsOf(t) == 1)
|
||||
}
|
||||
|
||||
// a struct this big passed or looped over by value is copied byte by byte each time
|
||||
fn bigCopy(ptr[Gen] g, ast.Type t) -> int {
|
||||
if t.kind != "struct" {
|
||||
return 0
|
||||
}
|
||||
n := byteSize(g, t)
|
||||
if n >= 256 {
|
||||
return n
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn emit(ptr[Gen] g, str line) {
|
||||
// with -g every call in a function that has debug info has to say where it is
|
||||
if g.dbgScope >= 0 && line.contains("call ") {
|
||||
|
|
@ -2306,6 +2360,10 @@ fn define(ptr[Gen] g, int si) {
|
|||
}
|
||||
for k in 0..g.cur.params.len {
|
||||
pt := g.cur.params[k]
|
||||
big := bigCopy(g, pt)
|
||||
if big > 0 {
|
||||
warn(g, f.kids[k], "'" + g.cur.pnames[k] + "' is a " + ast.show(pt) + " (" + str(big) + " bytes), copied on every call to " + f.name + "; take ptr[" + ast.show(pt) + "] to share it instead (callers can still pass the variable as it is)")
|
||||
}
|
||||
params.push(llty(g, pt) + " %p" + str(k))
|
||||
v := "%p" + str(k)
|
||||
if owning(g, pt) && !g.cur.borrow[k] {
|
||||
|
|
@ -2714,6 +2772,14 @@ fn eachStmt(ptr[Gen] g, ast.Node s) {
|
|||
et = lt.elem[0]
|
||||
}
|
||||
copyElems := r.isPlace && owning(g, et) && mutates(g, rootVar(s.kids[0]), s.kids[1])
|
||||
big := bigCopy(g, et)
|
||||
if big > 0 && s.text.len == 0 {
|
||||
list := "list"
|
||||
if s.kids[0].kind == "var" {
|
||||
list = s.kids[0].name
|
||||
}
|
||||
warn(g, s, "each " + ast.show(et) + " (" + str(big) + " bytes) is copied into '" + s.name + "'; for i in 0.." + list + ".len with " + list + "[i] works on it in place")
|
||||
}
|
||||
iv := alloca(g, "i64")
|
||||
store(g, "i64", "0", iv)
|
||||
cond := newLabel(g, "each.cond")
|
||||
|
|
|
|||
Loading…
Reference in a new issue