use cbuilder for default proc generation (#24390)

C++ member procs are not implemented, and `codegenDecl` in general is
also not adapted, although it had to be included in the generation of
proc params for simplicity. Guessing `codegenDecl` and C++ stuff are
supposed to map to pragmas on NIFC, or are just not supported.
This commit is contained in:
metagn
2024-11-01 01:02:14 +03:00
committed by GitHub
parent d55cd40642
commit 0e3330ee96
5 changed files with 342 additions and 156 deletions

View File

@@ -89,6 +89,17 @@ template addTypedef(builder: var Builder, name: string, typeBody: typed) =
builder.add(name)
builder.add(";\n")
proc addProcTypedef(builder: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
builder.add("typedef ")
builder.add(CallingConvToStr[callConv])
builder.add("_PTR(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
builder.add(";\n")
template addArrayTypedef(builder: var Builder, name: string, len: BiggestInt, typeBody: typed) =
## adds an array typedef declaration to the builder with name `name`,
## length `len`, and element type as built in `typeBody`
@@ -200,6 +211,16 @@ proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bo
obj.add(initializer)
obj.add(";\n")
proc addProcField(obj: var Builder, callConv: TCallingConvention, name: string, rettype, params: Snippet) =
obj.add(CallingConvToStr[callConv])
obj.add("_PTR(")
obj.add(rettype)
obj.add(", ")
obj.add(name)
obj.add(")")
obj.add(params)
obj.add(";\n")
type
BaseClassKind = enum
## denotes how and whether or not the base class/RTTI should be stored
@@ -349,17 +370,23 @@ template addAnonUnion(obj: var Builder; body: typed) =
obj.add("};\n")
type DeclVisibility = enum
None
Extern
ExternC
ImportLib
ExportLib
ExportLibVar
Private
StaticProc
template addDeclWithVisibility(builder: var Builder, visibility: DeclVisibility, declBody: typed) =
## adds a declaration as in `declBody` with the given visibility
case visibility
of None: discard
of Extern:
builder.add("extern ")
of ExternC:
builder.add("extern \"C\" ")
of ImportLib:
builder.add("N_LIB_IMPORT ")
of ExportLib:
@@ -368,4 +395,121 @@ template addDeclWithVisibility(builder: var Builder, visibility: DeclVisibility,
builder.add("N_LIB_EXPORT_VAR ")
of Private:
builder.add("N_LIB_PRIVATE ")
of StaticProc:
builder.add("static ")
declBody
type ProcParamBuilder = object
needsComma: bool
proc initProcParamBuilder(builder: var Builder): ProcParamBuilder =
result = ProcParamBuilder(needsComma: false)
builder.add("(")
proc finishProcParamBuilder(builder: var Builder, params: ProcParamBuilder) =
if params.needsComma:
builder.add(")")
else:
builder.add("void)")
template cgDeclFrmt*(s: PSym): string =
s.constraint.strVal
proc addParam(builder: var Builder, params: var ProcParamBuilder, name: string, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add(typ)
builder.add(" ")
builder.add(name)
proc addParam(builder: var Builder, params: var ProcParamBuilder, param: PSym, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
var modifiedTyp = typ
if sfNoalias in param.flags:
modifiedTyp.add(" NIM_NOALIAS")
if sfCodegenDecl notin param.flags:
builder.add(modifiedTyp)
builder.add(" ")
builder.add(param.loc.snippet)
else:
builder.add runtimeFormat(param.cgDeclFrmt, [modifiedTyp, param.loc.snippet])
proc addUnnamedParam(builder: var Builder, params: var ProcParamBuilder, typ: Snippet) =
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add(typ)
proc addVarargsParam(builder: var Builder, params: var ProcParamBuilder) =
# does not exist in NIFC, needs to be proc pragma
if params.needsComma:
builder.add(", ")
else:
params.needsComma = true
builder.add("...")
template addProcParams(builder: var Builder, params: out ProcParamBuilder, body: typed) =
params = initProcParamBuilder(builder)
body
finishProcParamBuilder(builder, params)
proc addProcHeader(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet, addAttributes: bool) =
# on nifc should build something like (proc name params type pragmas
# with no body given
let noreturn = isNoReturn(m, prc)
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(naked) ")
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(noreturn) ")
builder.add(CallingConvToStr[prc.typ.callConv])
builder.add("(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
if addAttributes:
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((naked))")
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((noreturn))")
proc finishProcHeaderAsProto(builder: var Builder) =
builder.add(";\n")
template finishProcHeaderWithBody(builder: var Builder, body: typed) =
builder.add(" {\n")
body
builder.add("}\n\n")
proc addProcVar(builder: var Builder, m: BModule, prc: PSym, name: string, params, rettype: Snippet,
isStatic = false, ignoreAttributes = false) =
# on nifc, builds full variable
if isStatic:
builder.add("static ")
let noreturn = isNoReturn(m, prc)
if not ignoreAttributes:
if sfPure in prc.flags and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(naked) ")
if noreturn and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
builder.add("__declspec(noreturn) ")
builder.add(CallingConvToStr[prc.typ.callConv])
builder.add("_PTR(")
builder.add(rettype)
builder.add(", ")
builder.add(name)
builder.add(")")
builder.add(params)
if not ignoreAttributes:
if sfPure in prc.flags and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((naked))")
if noreturn and hasAttribute in extccomp.CC[m.config.cCompiler].props:
builder.add(" __attribute__((noreturn))")
# ensure we are just adding a variable:
builder.add(";\n")

View File

@@ -23,8 +23,11 @@ const
"N_NOCONV" #ccMember is N_NOCONV
]
proc procPtrType(conv: TCallingConvention, rettype: Snippet, name: string): Snippet =
CallingConvToStr[conv] & "_PTR(" & rettype & ", " & name & ")"
proc procPtrTypeUnnamed(rettype, params: Snippet): Snippet =
rettype & "(*)" & params
proc procPtrTypeUnnamedNimCall(rettype, params: Snippet): Snippet =
rettype & "(N_RAW_NIMCALL*)" & params
proc cCast(typ, value: Snippet): Snippet =
"((" & typ & ") " & value & ")"

View File

@@ -92,3 +92,20 @@ template addElseBranch(builder: var Builder, stmt: var IfStmt, body: typed) =
builder.add(" else {\n")
body
builder.add("}")
template addScope(builder: var Builder, body: typed) =
builder.add("{")
body
builder.add("\t}")
proc addLabel(builder: var Builder, name: TLabel) =
builder.add(name)
builder.add(": ;\n")
proc addReturn(builder: var Builder) =
builder.add("return;\n")
proc addReturn(builder: var Builder, value: string) =
builder.add("return ")
builder.add(value)
builder.add(";\n")

View File

@@ -511,9 +511,6 @@ proc multiFormat*(frmt: var string, chars: static openArray[char], args: openArr
res.add(substr(frmt, start, i - 1))
frmt = res
template cgDeclFrmt*(s: PSym): string =
s.constraint.strVal
proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params: var string,
check: var IntSet, declareEnvironment=true;
weakDep=false;) =
@@ -590,79 +587,65 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
params.delete(params.len()-1..params.len()-1)
params.add("...)")
proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
proc genProcParams(m: BModule; t: PType, rettype: var Rope, params: var Builder,
check: var IntSet, declareEnvironment=true;
weakDep=false;) =
params = "("
if t.returnType == nil or isInvalidReturnType(m.config, t):
rettype = "void"
else:
rettype = getTypeDescAux(m, t.returnType, check, dkResult)
for i in 1..<t.n.len:
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
var param = t.n[i].sym
var descKind = dkParam
if m.config.backend == backendCpp and optByRef in param.options:
if param.typ.kind == tyGenericInst:
descKind = dkRefGenericParam
var paramBuilder: ProcParamBuilder
params.addProcParams(paramBuilder):
for i in 1..<t.n.len:
if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
var param = t.n[i].sym
var descKind = dkParam
if m.config.backend == backendCpp and optByRef in param.options:
if param.typ.kind == tyGenericInst:
descKind = dkRefGenericParam
else:
descKind = dkRefParam
if isCompileTimeOnly(param.typ): continue
fillParamName(m, param)
fillLoc(param.loc, locParam, t.n[i],
param.paramStorageLoc)
var typ: Rope
if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
typ = ptrType(getTypeDescWeak(m, param.typ, check, descKind))
incl(param.loc.flags, lfIndirect)
param.loc.storage = OnUnknown
elif weakDep:
typ = (getTypeDescWeak(m, param.typ, check, descKind))
else:
descKind = dkRefParam
if isCompileTimeOnly(param.typ): continue
if params != "(": params.add(", ")
fillParamName(m, param)
fillLoc(param.loc, locParam, t.n[i],
param.paramStorageLoc)
var typ: Rope
if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
typ = (getTypeDescWeak(m, param.typ, check, descKind))
typ.add("*")
incl(param.loc.flags, lfIndirect)
param.loc.storage = OnUnknown
elif weakDep:
typ = (getTypeDescWeak(m, param.typ, check, descKind))
else:
typ = (getTypeDescAux(m, param.typ, check, descKind))
typ.add(" ")
if sfNoalias in param.flags:
typ.add("NIM_NOALIAS ")
if sfCodegenDecl notin param.flags:
params.add(typ)
params.add(param.loc.snippet)
else:
params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.snippet])
# declare the len field for open arrays:
var arr = param.typ.skipTypes({tyGenericInst})
if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType
var j = 0
while arr.kind in {tyOpenArray, tyVarargs}:
# this fixes the 'sort' bug:
if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown
# need to pass hidden parameter:
params.addf(", NI $1Len_$2", [param.loc.snippet, j.rope])
inc(j)
arr = arr[0].skipTypes({tySink})
if t.returnType != nil and isInvalidReturnType(m.config, t):
var arr = t.returnType
if params != "(": params.add(", ")
if mapReturnType(m.config, arr) != ctArray:
if isHeaderFile in m.flags:
# still generates types for `--header`
params.add(getTypeDescAux(m, arr, check, dkResult))
params.add("*")
typ = (getTypeDescAux(m, param.typ, check, descKind))
params.addParam(paramBuilder, param, typ = typ)
# declare the len field for open arrays:
var arr = param.typ.skipTypes({tyGenericInst})
if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType
var j = 0
while arr.kind in {tyOpenArray, tyVarargs}:
# this fixes the 'sort' bug:
if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown
# need to pass hidden parameter:
params.addParam(paramBuilder, name = param.loc.snippet & "Len_" & $j, typ = "NI")
inc(j)
arr = arr[0].skipTypes({tySink})
if t.returnType != nil and isInvalidReturnType(m.config, t):
var arr = t.returnType
var typ: Snippet
if mapReturnType(m.config, arr) != ctArray:
if isHeaderFile in m.flags:
# still generates types for `--header`
typ = ptrType(getTypeDescAux(m, arr, check, dkResult))
else:
typ = ptrType(getTypeDescWeak(m, arr, check, dkResult))
else:
params.add(getTypeDescWeak(m, arr, check, dkResult))
params.add("*")
else:
params.add(getTypeDescAux(m, arr, check, dkResult))
params.addf(" Result", [])
if t.callConv == ccClosure and declareEnvironment:
if params != "(": params.add(", ")
params.add("void* ClE_0")
if tfVarargs in t.flags:
if params != "(": params.add(", ")
params.add("...")
if params == "(": params.add("void)")
else: params.add(")")
typ = getTypeDescAux(m, arr, check, dkResult)
params.addParam(paramBuilder, name = "Result", typ = typ)
if t.callConv == ccClosure and declareEnvironment:
params.addParam(paramBuilder, name = "ClE_0", typ = "void*")
if tfVarargs in t.flags:
params.addVarargsParam(paramBuilder)
proc mangleRecFieldName(m: BModule; field: PSym): Rope =
if {sfImportc, sfExportc} * field.flags != {}:
@@ -959,18 +942,17 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
of tyProc:
result = getTypeName(m, origTyp, sig)
m.typeCache[sig] = result
var rettype, desc: Rope = ""
var rettype: Snippet = ""
var desc = newBuilder("")
genProcParams(m, t, rettype, desc, check, true, true)
if not isImportedType(t):
var typedef = newBuilder("")
if t.callConv != ccClosure: # procedure vars may need a closure!
typedef.addTypedef(name = desc):
typedef.add(procPtrType(t.callConv, rettype = rettype, name = result))
typedef.addProcTypedef(callConv = t.callConv, name = result, rettype = rettype, params = desc)
else:
typedef.addTypedef(name = result):
typedef.addSimpleStruct(m, name = "", baseType = ""):
typedef.addField(name = desc, typ =
procPtrType(ccNimCall, rettype = rettype, name = "ClP_0"))
typedef.addProcField(name = "ClP_0", callConv = ccNimCall, rettype = rettype, params = desc)
typedef.addField(name = "ClE_0", typ = "void*")
m.s[cfsTypes].add(typedef)
of tySequence:
@@ -1124,18 +1106,17 @@ proc getClosureType(m: BModule; t: PType, kind: TClosureTypeKind): Rope =
assert t.kind == tyProc
var check = initIntSet()
result = getTempName(m)
var rettype, desc: Rope = ""
var rettype: Snippet = ""
var desc = newBuilder("")
genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
if not isImportedType(t):
var typedef = newBuilder("")
if t.callConv != ccClosure or kind != clFull:
typedef.addTypedef(name = desc):
typedef.add(procPtrType(t.callConv, rettype = rettype, name = result))
typedef.addProcTypedef(callConv = t.callConv, name = result, rettype = rettype, params = desc)
else:
typedef.addTypedef(name = result):
typedef.addSimpleStruct(m, name = "", baseType = ""):
typedef.addField(name = desc, typ =
procPtrType(ccNimCall, rettype = rettype, name = "ClP_0"))
typedef.addProcField(name = "ClP_0", callConv = ccNimCall, rettype = rettype, params = desc)
typedef.addField(name = "ClE_0", typ = "void*")
m.s[cfsTypes].add(typedef)
@@ -1228,34 +1209,38 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool =
[rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
params, fnConst, override, superCall])
proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) =
proc genProcHeader(m: BModule; prc: PSym; result: var Rope; visibility: var DeclVisibility, asPtr: bool, addAttributes: bool) =
# using static is needed for inline procs
var check = initIntSet()
fillBackendName(m, prc)
fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
var rettype, params: Rope = ""
var rettype: Snippet = ""
var params = newBuilder("")
genProcParams(m, prc.typ, rettype, params, check, true, false)
# handle the 2 options for hotcodereloading codegen - function pointer
# (instead of forward declaration) or header for function body with "_actual" postfix
let asPtrStr = rope(if asPtr: "_PTR" else: "")
var name = prc.loc.snippet
if not asPtr and isReloadable(m, prc):
name.add("_actual")
# careful here! don't access ``prc.ast`` as that could reload large parts of
# the object graph!
if sfCodegenDecl notin prc.flags:
var isStaticVar = false
if lfExportLib in prc.loc.flags:
if isHeaderFile in m.flags:
result.add "N_LIB_IMPORT "
visibility = ImportLib
else:
result.add "N_LIB_EXPORT "
elif prc.typ.callConv == ccInline or asPtr or isNonReloadable(m, prc):
result.add "static "
visibility = ExportLib
elif asPtr:
isStaticVar = true
elif prc.typ.callConv == ccInline or isNonReloadable(m, prc):
visibility = StaticProc
elif sfImportc notin prc.flags:
result.add "N_LIB_PRIVATE "
result.addf("$1$2($3, $4)$5",
[rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
params])
visibility = Private
if asPtr:
result.addProcVar(m, prc, name, params, rettype, isStatic = isStaticVar, ignoreAttributes = true)
else:
result.addProcHeader(m, prc, name, params, rettype, addAttributes)
else:
let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
@@ -1573,8 +1558,13 @@ include ccgtrav
proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
genProc(m, s)
m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
[result, s.loc.snippet])
var params = newBuilder("")
var paramBuilder: ProcParamBuilder
params.addProcParams(paramBuilder):
params.addUnnamedParam(paramBuilder, typ = "void*")
let pt = procPtrTypeUnnamedNimCall(rettype = "void*", params = params)
m.s[cfsTypeInit3].addFieldAssignmentWithValue(result, "deepcopy"):
m.s[cfsTypeInit3].add(cCast(pt, s.loc.snippet))
proc declareNimType(m: BModule; name: string; str: Rope, module: int) =
let nr = rope(name)
@@ -1590,7 +1580,8 @@ proc declareNimType(m: BModule; name: string; str: Rope, module: int) =
m.s[cfsTypeInit1].addArgument(hcrGlobal):
m.s[cfsTypeInit1].add("\"" & str & "\"")
else:
m.s[cfsStrData].addf("extern $2 $1;$n", [str, nr])
m.s[cfsStrData].addDeclWithVisibility(Extern):
m.s[cfsStrData].addVar(kind = Local, name = str, typ = nr)
proc genTypeInfo2Name(m: BModule; t: PType): Rope =
var it = t

View File

@@ -337,6 +337,9 @@ proc getTempName(m: BModule): Rope =
result = m.tmpBase & rope(m.labels)
inc m.labels
proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
sfNoReturn in s.flags and m.config.exc != excGoto
include cbuilderbase
include cbuilderexprs
include cbuilderdecls
@@ -759,7 +762,7 @@ proc getLabel(p: BProc): TLabel =
result = "LA" & rope(p.labels) & "_"
proc fixLabel(p: BProc, labl: TLabel) =
p.s(cpsStmts).add("$1: ;$n" % [labl])
p.s(cpsStmts).addLabel(labl)
proc genVarPrototype(m: BModule, n: PNode)
proc requestConstImpl(p: BProc, sym: PSym)
@@ -1181,29 +1184,30 @@ proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
proc getProcTypeCast(m: BModule, prc: PSym): Rope =
result = getTypeDesc(m, prc.loc.t)
if prc.typ.callConv == ccClosure:
var rettype, params: Rope = ""
var rettype: Snippet = ""
var params = newBuilder("")
var check = initIntSet()
genProcParams(m, prc.typ, rettype, params, check)
result = "$1(*)$2" % [rettype, params]
result = procPtrTypeUnnamed(rettype = rettype, params = params)
proc genProcBody(p: BProc; procBody: PNode) =
genStmts(p, procBody) # modifies p.locals, p.init, etc.
if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}:
p.flags.incl nimErrorFlagDeclared
p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", []))
p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))
proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
sfNoReturn in s.flags and m.config.exc != excGoto
p.blocks[0].sections[cpsLocals].addVar(kind = Local,
name = "nimErr_", typ = ptrType("NIM_BOOL"))
p.blocks[0].sections[cpsInit].addAssignmentWithValue("nimErr_"):
p.blocks[0].sections[cpsInit].addCall(cgsymValue(p.module, "nimErrorFlag"))
proc genProcAux*(m: BModule, prc: PSym) =
var p = newProc(prc, m)
var header = newRopeAppender()
let isCppMember = m.config.backend == backendCpp and sfCppMember * prc.flags != {}
var visibility: DeclVisibility = None
if isCppMember:
genMemberProcHeader(m, prc, header)
else:
genProcHeader(m, prc, header)
genProcHeader(m, prc, header, visibility, asPtr = false, addAttributes = false)
var returnStmt: Rope = ""
assert(prc.ast != nil)
@@ -1224,7 +1228,8 @@ proc genProcAux*(m: BModule, prc: PSym) =
if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
var decl = localVarDecl(p, resNode)
var a: TLoc = initLocExprSingleUse(p, val)
linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)])
let ra = rdLoc(a)
p.s(cpsStmts).addAssignment(decl, ra)
else:
# declare the result symbol:
assignLocalVar(p, resNode)
@@ -1236,7 +1241,9 @@ proc genProcAux*(m: BModule, prc: PSym) =
discard "result init optimized out"
else:
initLocalVar(p, res, immediateAsgn=false)
returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
returnStmt = "\t"
let rres = rdLoc(res.loc)
returnStmt.addReturn(rres)
elif sfConstructor in prc.flags:
resNode.sym.loc.flags.incl lfIndirect
fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
@@ -1267,45 +1274,56 @@ proc genProcAux*(m: BModule, prc: PSym) =
prc.info = tmpInfo
var generatedProc: Rope = ""
var generatedProc = newBuilder("")
generatedProc.genCLineDir prc.info, m.config
if isNoReturn(p.module, prc):
if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
header = "__declspec(noreturn) " & header
if sfPure in prc.flags:
if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
header = "__declspec(naked) " & header
generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N",
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
else:
if m.hcrOn and isReloadable(m, prc):
# Add forward declaration for "_actual"-suffixed functions defined in the same module (or inline).
# This fixes the use of methods and also the case when 2 functions within the same module
# call each other using directly the "_actual" versions (an optimization) - see issue #11608
m.s[cfsProcHeaders].addf("$1;\n", [header])
generatedProc.add ropecg(p.module, "$1 {$n", [header])
if optStackTrace in prc.options:
generatedProc.add(p.s(cpsLocals))
var procname = makeCString(prc.name.s)
generatedProc.add(initFrame(p, procname, quotedFilename(p.config, prc.info)))
generatedProc.addDeclWithVisibility(visibility):
if sfPure in prc.flags:
generatedProc.add(header)
generatedProc.finishProcHeaderWithBody():
generatedProc.add(p.s(cpsLocals))
generatedProc.add(p.s(cpsInit))
generatedProc.add(p.s(cpsStmts))
else:
generatedProc.add(p.s(cpsLocals))
if optProfiler in prc.options:
# invoke at proc entry for recursion:
appcg(p, cpsInit, "\t#nimProfile();$n", [])
# this pair of {} is required for C++ (C++ is weird with its
# control flow integrity checks):
if beforeRetNeeded in p.flags: generatedProc.add("{")
generatedProc.add(p.s(cpsInit))
generatedProc.add(p.s(cpsStmts))
if beforeRetNeeded in p.flags: generatedProc.add("\t}BeforeRet_: ;\n")
if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
generatedProc.add(returnStmt)
generatedProc.add("}\n")
if m.hcrOn and isReloadable(m, prc):
m.s[cfsProcHeaders].addDeclWithVisibility(visibility):
# Add forward declaration for "_actual"-suffixed functions defined in the same module (or inline).
# This fixes the use of methods and also the case when 2 functions within the same module
# call each other using directly the "_actual" versions (an optimization) - see issue #11608
m.s[cfsProcHeaders].add(header)
m.s[cfsProcHeaders].finishProcHeaderAsProto()
generatedProc.add(header)
generatedProc.finishProcHeaderWithBody():
if optStackTrace in prc.options:
generatedProc.add(p.s(cpsLocals))
var procname = makeCString(prc.name.s)
generatedProc.add(initFrame(p, procname, quotedFilename(p.config, prc.info)))
else:
generatedProc.add(p.s(cpsLocals))
if optProfiler in prc.options:
# invoke at proc entry for recursion:
p.s(cpsInit).add('\t')
p.s(cpsInit).addCallStmt(cgsymValue(m, "nimProfile"))
if beforeRetNeeded in p.flags:
# this pair of {} is required for C++ (C++ is weird with its
# control flow integrity checks):
generatedProc.addScope():
generatedProc.add(p.s(cpsInit))
generatedProc.add(p.s(cpsStmts))
generatedProc.addLabel("BeforeRet_")
else:
generatedProc.add(p.s(cpsInit))
generatedProc.add(p.s(cpsStmts))
if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
generatedProc.add(returnStmt)
m.s[cfsProcs].add(generatedProc)
if isReloadable(m, prc):
m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
[prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
m.s[cfsDynLibInit].add('\t')
m.s[cfsDynLibInit].addAssignmentWithValue(prc.loc.snippet):
m.s[cfsDynLibInit].addCast(getProcTypeCast(m, prc)):
m.s[cfsDynLibInit].addCall("hcrRegisterProc",
getModuleDllPath(m, prc),
'"' & prc.loc.snippet & '"',
cCast("void*", prc.loc.snippet & "_actual"))
proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
result = (sfCompileToCpp in m.module.flags and
@@ -1322,26 +1340,39 @@ proc genProcPrototype(m: BModule, sym: PSym) =
if lfDynamicLib in sym.loc.flags:
if sym.itemId.module != m.module.position and
not containsOrIncl(m.declaredThings, sym.id):
m.s[cfsVars].add(ropecg(m, "$1 $2 $3;$n",
[(if isReloadable(m, sym): "static" else: "extern"),
getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)]))
let vis = if isReloadable(m, sym): StaticProc else: Extern
let name = mangleDynLibProc(sym)
let t = getTypeDesc(m, sym.loc.t)
m.s[cfsVars].addDeclWithVisibility(vis):
m.s[cfsVars].addVar(kind = Local,
name = name,
typ = t)
if isReloadable(m, sym):
m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
[mangleDynLibProc(sym), getTypeDesc(m, sym.loc.t), getModuleDllPath(m, sym)])
m.s[cfsDynLibInit].add('\t')
m.s[cfsDynLibInit].addAssignmentWithValue(name):
m.s[cfsDynLibInit].addCast(t):
m.s[cfsDynLibInit].addCall("hcrGetProc",
getModuleDllPath(m, sym),
'"' & name & '"')
elif not containsOrIncl(m.declaredProtos, sym.id):
let asPtr = isReloadable(m, sym)
var header = newRopeAppender()
genProcHeader(m, sym, header, asPtr)
if not asPtr:
if isNoReturn(m, sym) and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
header = "__declspec(noreturn) " & header
if sym.typ.callConv != ccInline and requiresExternC(m, sym):
header = "extern \"C\" " & header
if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
header.add(" __attribute__((naked))")
if isNoReturn(m, sym) and hasAttribute in CC[m.config.cCompiler].props:
header.add(" __attribute__((noreturn))")
m.s[cfsProcHeaders].add(ropecg(m, "$1;$N", [header]))
var visibility: DeclVisibility = None
genProcHeader(m, sym, header, visibility, asPtr = asPtr, addAttributes = true)
if asPtr:
m.s[cfsProcHeaders].addDeclWithVisibility(visibility):
# genProcHeader would give variable declaration, add it directly
m.s[cfsProcHeaders].add(header)
else:
let extraVis =
if sym.typ.callConv != ccInline and requiresExternC(m, sym):
ExternC
else:
None
m.s[cfsProcHeaders].addDeclWithVisibility(extraVis):
m.s[cfsProcHeaders].addDeclWithVisibility(visibility):
m.s[cfsProcHeaders].add(header)
m.s[cfsProcHeaders].finishProcHeaderAsProto()
# TODO: figure out how to rename this - it DOES generate a forward declaration
proc genProcNoForward(m: BModule, prc: PSym) =