Repository navigation
@cppvtable: Itanium ABI calls (x86-64, AArch64): struct returns with @cppnontrivial, narrow arguments extended - #553
Merged
Conversation
The Itanium ABI returns a record with a destructor or a copy constructor through a hidden pointer passed before 'this' - marked @cppnontrivial, as a tuple can't say so - and a trivial record of one or two 8-byte integer or pointer fields in RAX:RDX, as tslang returns such a tuple. On x86-64 a @cppvtable method returning a tuple now follows that: a @cppnontrivial method's slot is called as (Ref<T>, this, args...), an unmarked one keeps its declared type, and any other record is an error rather than a wrong call. AArch64 (hidden pointer in X8) stays an error. DeclarationPrinter keeps @cppnontrivial for an importer. Tests: test-jit-cppvtable-abi calls the methods of an object made by the host's C++ compiler (cppvtable/abi_host.cpp, a shared library) - MSVC's ABI on Windows, g++'s on x86-64 Linux - with records with and without a destructor, of 8 and 16 bytes; errors for an unsupported record, @cppnontrivial on a method returning a number, and AArch64. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r marked sret AArch64 passes the hidden pointer a record is returned through in X8, not as the first argument. The call now marks it sret (ts.sret_arg on CallIndirect, carried to CallInternal or Invoke, turned into llvm.sret by LowerToLLVM), so the backend passes it where the ABI does: X8 on AArch64, RDI on x86-64 as before. MSVC's pointer after 'this' stays an ordinary argument (Windows ARM64 passes it in X1). Checked against NDK clang's code for the same C++ calls on aarch64-linux-android (X8 = the result, X0 = this, X0:X1 for a 16-byte trivial record), and again with g++ on x86-64 Linux (WSL). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…signext) A C++ caller extends an argument narrower than 32 bits - a bool, an uint8_t enum - and clang's code relies on it; tslang's calls left the register's upper bits undefined. A call of a @cppvtable method is now marked (ts.cpp_call) and LowerToLLVM gives such arguments zeroext, or signext for a signed type. The marking goes through mlirGenCallFunction's new callAttrs, as ts.sret_arg does now. Tests: abi_host.cpp's narrow(bool, uint8_t, int8_t), called with values made from a wider one (abi_msvc.ts, abi_itanium.ts; abi_itanium.ts also run against g++'s code in WSL); test-compile-cppvtable-itanium-call-<triple> checks the IR's zeroext / signext and sret for x86-64 Linux and AArch64 Android. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
export_cppvtable_hidden_return.ts's pair() is @cppnontrivial: DeclarationPrinter keeps it in the module's declarations (__decls), and the importer reads it back. MSVC ignores it, so the Windows-only test calls the same slot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ASDAlexander77
force-pushed
the
cppvtable-itanium-return
branch
from
October 11, 2026 21:16
d270a2d to
f876122
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A
@cppvtablemethod returning a struct by value now also works under the Itanium C++ ABI (g++, clang) on x86-64 and AArch64, which covers Linux and Android. Until now it was an error on any non-MSVC target.The Itanium ABI decides by the struct:
nvrhi::RefCountPtr<T>this, as the call'ssretargument (RDI on x86-64, X8 on AArch64)@cppnontrivial createBuffer(desc: Ref<BufferDesc>): RefCountPtr;nvrhi::ObjectgetNativeObject(type: u32): NativeObject;, with no markA tuple can't express whether the C++ type has a destructor, so
@cppnontrivialsays so. MSVC ignores it, since MSVC always passes the pointer afterthis. Methods on any other target (for example 32-bit x86) are still an error.DeclarationPrinterkeeps@cppnontrivial, so modules that import the class see it too.Changes
TargetInfo::hiddenReturnPointerBeforeThis: set for non-MSVC x86-64 and AArch64 targets.mlirGenClassCppVTableHiddenReturn: lowers@cppnontrivialmethods to(this, Ref<T>, args) -> Ref<T>, the same as MSVC, and records that the pointer goes beforethis. Unmarked methods returning one or two 8-byte fields keep their declared type. It reports errors for other structs and for@cppnontrivialon a method that doesn't return a struct.mlirGenCall): calls the slot as(Ref<T>, this, args...)and tags the call withts.sret_arg.CallIndirecttoCallInternalorInvoke, andLowerToLLVMturns it intollvm.sret = Ton the argument. That is what moves it to X8 on AArch64. MSVC's pointer afterthisstays an ordinary argument; Windows ARM64 passes it in X1.ts.cpp_call): every call of a@cppvtablemethod is tagged, andLowerToLLVMmarks its arguments narrower than 32 bitszeroext(orsignextif signed), booleans included. C++ callers extend them this way and clang-compiled callees rely on it; before this, tslang's calls left the register's upper bits undefined. MSVC callees don't rely on it, so this is harmless there, and a C++boolcan now be declaredbooleanrather thanu8. Both tags reach the call through a newcallAttrsparameter ofmlirGenCallFunction.Tests
test-jit-cppvtable-abi(new): checks against code from a real C++ compiler, not a vtable built by hand in tslang.cppvtable/abi_host.cppis built as a shared library by the host's C++ compiler (MSVC on Windows, g++ or clang on x86-64 or AArch64 Linux). The JIT then calls the object's methods through a@cppvtableclass,abi_msvc.tsorabi_itanium.ts, which differ in their destructor slots. It covers a struct with a destructor, trivial 8- and 16-byte structs, a plainint, andnarrow(bool, uint8_t, int8_t)called with values truncated from a wider one. If the slots are shifted by one, it fails.test-compile-cppvtable-itanium-call-<triple>(new): for x86-64 Linux and AArch64 Android, checks that the IR haszeroext/signexton the narrow arguments andsreton the hidden pointer. It runs on any host.cppvtable-itanium-record: an unmarked[s32, s32]on x86-64 Linux;cppvtable-nontrivial-no-struct:@cppnontrivialon a method returning a number;cppvtable-hidden-return-target: now uses i686 Linux.main. After rebasing ontomain(with @cppvtable: methods returning a record through MSVC's hidden pointer #551 and import: a module's file for the target, <name>.<os>.ts, before <name>.ts #552 squash-merged), the 322@cppvtable, import/export,<os>.tsimport and ownership tests pass.x86_64-unknown-linux-gnu, linked it with a g++-built C++ object in WSL Ubuntu, and ran it. All four kinds of method returned the right values, both before and after thesretchange.cppvtable/abi_itanium.tsitself, linked with a g++-builtabi_host.cpp, also printsdone.there.aarch64-linux-android29with NDK r30 clang's code for the same C++ calls. Both put the result pointer in X8 andthisin X0, and use the same vtable offsets (Integer types #16/not implemented forEach #24/check if the following code is working #32). The 16-byte result comes back in X0:X1. Calls insidetry(invoke) carrysrettoo.Docs
docs/cppvtable-classes.md: the Itanium rules,@cppnontrivial, and how to get Itanium slot numbers with clang (-fdump-vtable-layouts; slot N is entry N + 2).🤖 Generated with Claude Code