From 77cc882fcfe83930f6439804eeb3c3b7cfd3a95b Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:28:39 +0000 Subject: [PATCH 1/2] support `method`s with the VM backend Summary ======= Enable dispatcher generation for the VM backend, which makes `method` (including multi-methods) available for the standalone VM target. Note that this doesn't apply to compile-time execution, where using `method`s is still unsupported. Details ======= * move the `chckNilDisp` compilerproc into the `system` module and replace the `== nil` with `isNil` (more on that later) * only report errors on method usage from `vmgne` when not in the `standalone` mode * generate the method dispatcher prior to generating code in `vmbackend` The VM code generator (`vmgen`) previously checked for whether a procedure returns something by testing for the presence of a procedure's `result` AST slot. For methods - where the AST always has a `result` slot, regardless of whether they have a return value - this test is no longer enough, and is replaced with inspecting the return type. Regarding the `== nil` -> `isNil` change: the VM comparison operator for pointer-like types (which `==` maps to) requires both operands to be of same dynamic type, but in `chckNilDisp`, this is not necessarily the case (for example, when the dispatched-through type is a `ref` type). The proper solution would be for `cgmeth.genDispatcher` to wrap the argument in a `cast[pointer]`, but the VM doesn't yet support casting references to pointers. --- compiler/vm/vmbackend.nim | 9 ++++----- compiler/vm/vmgen.nim | 20 ++++++++++--------- lib/system.nim | 3 +++ lib/system/chcks.nim | 4 ---- lib/system/jssys.nim | 4 ---- .../lang_callable/generics/tobjecttyperel.nim | 4 +++- .../lang_callable/method/tgeneric_methods.nim | 3 --- tests/lang_callable/method/tmethod_issues.nim | 2 -- .../lang_callable/method/tmethod_various.nim | 3 --- tests/lang_callable/method/tmultim.nim | 4 ++-- tests/lang_callable/method/tnildispatcher.nim | 4 +--- tests/lang_callable/method/treturn_var_t.nim | 3 --- .../lang_callable/method/tsingle_methods.nim | 3 --- 13 files changed, 24 insertions(+), 42 deletions(-) diff --git a/compiler/vm/vmbackend.nim b/compiler/vm/vmbackend.nim index 60488112b82..da3992f69d4 100644 --- a/compiler/vm/vmbackend.nim +++ b/compiler/vm/vmbackend.nim @@ -22,7 +22,8 @@ import lineinfos ], compiler/backend/[ - backends + backends, + cgmeth ], compiler/front/[ msgs, @@ -357,14 +358,12 @@ proc generateCode*(g: ModuleGraph, mlist: sink ModuleList) = for s in m.structs.threadvars.items: declareGlobal(s) + generateMethodDispatchers(g) + # generate code for all alive routines generateAliveProcs(c, mlist) reset(c.linkState.newProcs) # free the occupied memory already - # XXX: generation of method dispatchers would go here. Note that `method` - # support will require adjustments to DCE handling - #generateMethods(c) - # create procs from the global initializer code fragments for m in mlist.modules.mitems: template frag: untyped = m.initGlobalsCode diff --git a/compiler/vm/vmgen.nim b/compiler/vm/vmgen.nim index 836228997dd..cc6457884cd 100644 --- a/compiler/vm/vmgen.nim +++ b/compiler/vm/vmgen.nim @@ -186,6 +186,11 @@ proc genRegLoad(c: var TCtx, n: PNode, dest, src: TRegister) template isUnset(x: TDest): bool = x < 0 +proc realType(s: PSym): PType {.inline.} = + ## Returns the signature type of the routine `s` + if s.kind == skMacro: s.internal + else: s.typ + func underlyingLoc(n: PNode): PSym = ## Computes and returns the symbol of the complete location (i.e., a location ## not part of a compound location) that l-value expression `n` names. If no @@ -942,7 +947,7 @@ proc writeBackResult(c: var TCtx, info: PNode) = ## If the result value fits into a register but is not stored in one ## (because it has its address taken, etc.), emits the code for storing it ## back into a register. `info` is only used to provide line information. - if resultPos < c.prc.sym.ast.len: + if not isEmptyType(c.prc.sym.realType[0]): let res = c.prc.sym.ast[resultPos] typ = res.typ @@ -3011,7 +3016,9 @@ proc gen(c: var TCtx; n: PNode; dest: var TDest) = let s = n[0].sym if s.magic != mNone: genMagic(c, n, dest, s.magic) - elif s.kind == skMethod: + elif s.kind == skMethod and c.mode != emStandalone: + # XXX: detect and reject this earlier -- it's not a code + # generation error fail(n.info, vmGenDiagCannotCallMethod, sym = s) else: genCall(c, n, dest) @@ -3180,20 +3187,15 @@ proc genExpr*(c: var TCtx; n: PNode): Result[TRegister, VmGenDiag] = result = typeof(result).ok(TRegister(d)) -proc realType(s: PSym): PType {.inline.} = - ## Returns the signature type of the routine `s` - if s.kind == skMacro: s.internal - else: s.typ proc genParams(prc: PProc; s: PSym) = let params = s.realType.n - res = if resultPos < s.ast.len: s.ast[resultPos] else: nil setLen(prc.regInfo, max(params.len, 1)) - if res != nil: - prc.locals[res.sym.id] = 0 + if not isEmptyType(s.realType[0]): + prc.locals[s.ast[resultPos].sym.id] = 0 prc.regInfo[0] = RegInfo(refCount: 1, kind: slotFixedVar) for i in 1..= b: a else: b -proc chckNilDisp(p: pointer) {.compilerproc.} = - if p == nil: - sysFatal(NilAccessDefect, "cannot dispatch; dispatcher is nil") - include "system/hti" proc isFatPointer(ti: PNimType): bool = diff --git a/tests/lang_callable/generics/tobjecttyperel.nim b/tests/lang_callable/generics/tobjecttyperel.nim index 4db138fdeb2..5842a508476 100644 --- a/tests/lang_callable/generics/tobjecttyperel.nim +++ b/tests/lang_callable/generics/tobjecttyperel.nim @@ -9,7 +9,9 @@ cool test''' """ -# disabled on VM until it supports methods (knownIssue) +# knownIssue: fails for the VM target because ``astgen`` emits the +# incorrect conversion operator for ``ref`` types involving +# generics # bug #5241 type diff --git a/tests/lang_callable/method/tgeneric_methods.nim b/tests/lang_callable/method/tgeneric_methods.nim index aa891edd428..f00e5495d2d 100644 --- a/tests/lang_callable/method/tgeneric_methods.nim +++ b/tests/lang_callable/method/tgeneric_methods.nim @@ -1,12 +1,9 @@ discard """ - target: !vm output: '''wow2 X 1 X 3''' """ -# disabled on VM until we support methods (knownIssue) - type First[T] = ref object of RootObj value: T diff --git a/tests/lang_callable/method/tmethod_issues.nim b/tests/lang_callable/method/tmethod_issues.nim index 0717e05d6a8..9283701d764 100644 --- a/tests/lang_callable/method/tmethod_issues.nim +++ b/tests/lang_callable/method/tmethod_issues.nim @@ -1,12 +1,10 @@ discard """ - target: "!vm" output: ''' wof! wof! ''' """ -# disabled on VM until we support methods (knownIssue) # bug #1659 diff --git a/tests/lang_callable/method/tmethod_various.nim b/tests/lang_callable/method/tmethod_various.nim index 6ac48771180..fd022717bb7 100644 --- a/tests/lang_callable/method/tmethod_various.nim +++ b/tests/lang_callable/method/tmethod_various.nim @@ -1,13 +1,10 @@ discard """ - target: "!vm" output: ''' do nothing HELLO WORLD! ''' """ -# disabled on VM until we support methods (knownIssue) - # tmethods1 method somethin(obj: RootObj) {.base.} = diff --git a/tests/lang_callable/method/tmultim.nim b/tests/lang_callable/method/tmultim.nim index 1d8cda2d1af..69e8f7db2ff 100644 --- a/tests/lang_callable/method/tmultim.nim +++ b/tests/lang_callable/method/tmultim.nim @@ -13,8 +13,8 @@ do nothing ''' """ -# disabled on VM until we support methods (knownIssue) - +# knownIssue: the ``of`` operator only considers the static type when using +# the VM backend # tmultim2 type diff --git a/tests/lang_callable/method/tnildispatcher.nim b/tests/lang_callable/method/tnildispatcher.nim index d5d1a910c61..5d59f46fef1 100644 --- a/tests/lang_callable/method/tnildispatcher.nim +++ b/tests/lang_callable/method/tnildispatcher.nim @@ -1,11 +1,9 @@ discard """ - target: "!vm !js" + target: "!js" outputsub: '''Error: unhandled exception: cannot dispatch; dispatcher is nil [NilAccessDefect]''' exitcode: 1 """ -# disabled on VM until we support methods (knownIssue) - # disabled on JS because sem/codegen lets it through and we get a runtime NPE # bug #5599 diff --git a/tests/lang_callable/method/treturn_var_t.nim b/tests/lang_callable/method/treturn_var_t.nim index 53a5c9a501d..91d9829028e 100644 --- a/tests/lang_callable/method/treturn_var_t.nim +++ b/tests/lang_callable/method/treturn_var_t.nim @@ -1,11 +1,8 @@ discard """ - target: "!vm" output: '''Inh 45''' """ -# disabled on VM until we support methods (knownIssue) - type Base = ref object of RootObj field: int diff --git a/tests/lang_callable/method/tsingle_methods.nim b/tests/lang_callable/method/tsingle_methods.nim index 3f1ac467d3d..2838e2e8d8e 100644 --- a/tests/lang_callable/method/tsingle_methods.nim +++ b/tests/lang_callable/method/tsingle_methods.nim @@ -1,6 +1,5 @@ discard """ matrix: "--multimethods:off" - target: "!vm" output: '''base base base @@ -10,8 +9,6 @@ base ''' """ -# disabled on VM until we support methods (knownIssue) - # bug #10912 type From 08fa2fa2f1cb3307cc3b38fcfc46b7c895921601 Mon Sep 17 00:00:00 2001 From: zerbina <100542850+zerbina@users.noreply.github.com> Date: Tue, 27 Jun 2023 19:11:36 +0000 Subject: [PATCH 2/2] vmgen: rename `realType` to `routineSignature` --- compiler/vm/vmgen.nim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/vm/vmgen.nim b/compiler/vm/vmgen.nim index cc6457884cd..321ee24e069 100644 --- a/compiler/vm/vmgen.nim +++ b/compiler/vm/vmgen.nim @@ -186,7 +186,7 @@ proc genRegLoad(c: var TCtx, n: PNode, dest, src: TRegister) template isUnset(x: TDest): bool = x < 0 -proc realType(s: PSym): PType {.inline.} = +proc routineSignature(s: PSym): PType {.inline.} = ## Returns the signature type of the routine `s` if s.kind == skMacro: s.internal else: s.typ @@ -947,7 +947,7 @@ proc writeBackResult(c: var TCtx, info: PNode) = ## If the result value fits into a register but is not stored in one ## (because it has its address taken, etc.), emits the code for storing it ## back into a register. `info` is only used to provide line information. - if not isEmptyType(c.prc.sym.realType[0]): + if not isEmptyType(c.prc.sym.routineSignature[0]): let res = c.prc.sym.ast[resultPos] typ = res.typ @@ -3190,11 +3190,11 @@ proc genExpr*(c: var TCtx; n: PNode): Result[TRegister, VmGenDiag] = proc genParams(prc: PProc; s: PSym) = let - params = s.realType.n + params = s.routineSignature.n setLen(prc.regInfo, max(params.len, 1)) - if not isEmptyType(s.realType[0]): + if not isEmptyType(s.routineSignature[0]): prc.locals[s.ast[resultPos].sym.id] = 0 prc.regInfo[0] = RegInfo(refCount: 1, kind: slotFixedVar) @@ -3274,7 +3274,7 @@ proc prepareParameters(c: var TCtx, info: PNode) = ## Prepares immutable parameters backed by registers for having their address ## taken. If an immutable parameter has its address taken, it is transitioned ## to a VM memory location at the start of the procedure. - let typ = c.prc.sym.realType # the procedure's type + let typ = c.prc.sym.routineSignature # the procedure's type template setupParam(c: var TCtx, s: PSym) = if fitsRegister(s.typ) and s.id in c.prc.addressTaken: @@ -3312,7 +3312,7 @@ proc genProcBody(c: var TCtx; s: PSym, body: PNode): int = # iterate over the parameters and allocate space for them: genParams(c.prc, s) - if s.realType.callConv == ccClosure: + if s.routineSignature.callConv == ccClosure: # reserve a slot for the hidden environment parameter and register the # mapping c.prc.regInfo.add RegInfo(refCount: 1, kind: slotFixedLet) @@ -3322,7 +3322,7 @@ proc genProcBody(c: var TCtx; s: PSym, body: PNode): int = # result register is setup at the start of macro evaluation # XXX: initializing the ``result`` of a macro should be handled through # inserting the necessary code either in ``sem` or here - let rt = s.realType[0] + let rt = s.routineSignature[0] if not isEmptyType(rt) and fitsRegister(rt): # initialize the register holding the result let r = s.ast[resultPos].sym