Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions tslang/include/TypeScript/MLIRLogic/MLIRPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ class MLIRPrinter
printAttribute(out, id, true);
}

// a C variadic function type (`@varargs`) reads back as `CVarArgs<(...) => ...>`; an arrow
// type alone would read back as a TypeScript function, called another way
template <typename T, typename F>
void printCVarArgsOrFuncType(T &out, F t)
{
if (t.isCVarArg())
{
out << "CVarArgs<";
printFuncType(out, t);
out << ">";
return;
}

printFuncType(out, t);
}

template <typename T, typename F>
void printFuncType(T &out, F t)
{
Expand Down Expand Up @@ -223,7 +239,10 @@ class MLIRPrinter
// it would parse back as HybridFunctionType instead - a different,
// wider (two-pointer-struct vs single-pointer) storage layout that
// silently misaligns every subsequent read through the field.
if (allowMethodSignature && field.id && isa<mlir_ts::FunctionType>(field.type))
// A C variadic function field is a value, not a method: it prints as `CVarArgs<...>`,
// which reads back as the same one-pointer type.
if (allowMethodSignature && field.id && isa<mlir_ts::FunctionType>(field.type)
&& !mlir::cast<mlir_ts::FunctionType>(field.type).isCVarArg())
{
printFieldName(out, field.id);
printFuncTypeAsMethodSignature(out, mlir::cast<mlir_ts::FunctionType>(field.type));
Expand Down Expand Up @@ -408,10 +427,10 @@ class MLIRPrinter
out << t.getName().getValue().str().c_str();
})
.template Case<mlir_ts::FunctionType>([&](auto t) {
printFuncType(out, t);
printCVarArgsOrFuncType(out, t);
})
.template Case<mlir_ts::HybridFunctionType>([&](auto t) {
printFuncType(out, t);
printCVarArgsOrFuncType(out, t);
})
.template Case<mlir_ts::ConstructFunctionType>([&](auto t) {
out << "new ";
Expand Down
26 changes: 25 additions & 1 deletion tslang/include/TypeScript/MLIRLogic/MLIRTypeHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,23 @@ class MLIRTypeHelper
return isVarArg;
}

// a C variadic function (`@varargs`), held as a function or a hybrid function
bool getCVarArgFromFuncRef(mlir::Type funcType)
{
funcType = stripOptionalType(funcType);
if (auto func = dyn_cast<mlir_ts::FunctionType>(funcType))
{
return func.isCVarArg();
}

if (auto hybridFunc = dyn_cast<mlir_ts::HybridFunctionType>(funcType))
{
return hybridFunc.isCVarArg();
}

return false;
}

mlir::Type getOmitThisFunctionTypeFromFuncRef(mlir::Type funcType)
{
mlir::Type paramsType;
Expand Down Expand Up @@ -1806,6 +1823,13 @@ class MLIRTypeHelper
auto leftIsVarArg = getVarArgFromFuncRef(typeLeft);
auto rightIsVarArg = getVarArgFromFuncRef(typeRight);

// a C variadic function and a TypeScript one are called differently: no common type
auto isCVarArg = getCVarArgFromFuncRef(typeLeft);
if (isCVarArg != getCVarArgFromFuncRef(typeRight))
{
return mlir::Type();
}

auto hybridFuncIsNeeded = false;
if (!isLeftTypeFunc)
{
Expand Down Expand Up @@ -1844,7 +1868,7 @@ class MLIRTypeHelper
return typeLeft;
}

return mlir_ts::HybridFunctionType::get(context, leftInputs, leftResults, leftIsVarArg);
return mlir_ts::HybridFunctionType::get(context, leftInputs, leftResults, leftIsVarArg, isCVarArg);
}

return mlir::Type();
Expand Down
40 changes: 28 additions & 12 deletions tslang/include/TypeScript/TypeScriptTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -764,24 +764,33 @@ def TypeScript_Function : TypeScript_Type<"Function"> {
let summary = "Map from a list of inputs to a list of results";
let description = [{
}];
let parameters = (ins ArrayRefParameter<"Type", "type">:$inputs, ArrayRefParameter<"Type", "type">:$results, "bool":$isVarArg);
// `isVarArg`: the last parameter is a TypeScript rest parameter, which takes the extra
// arguments packed into an array. `isCVarArg`: a C variadic function, declared
// `@varargs declare function printf(format: string)`; its extra arguments go to C's `...`, so
// a call through a value of this type is a variadic call. Printed only when set.
let parameters = (ins ArrayRefParameter<"Type", "type">:$inputs, ArrayRefParameter<"Type", "type">:$results, "bool":$isVarArg,
DefaultValuedParameter<"bool", "false">:$isCVarArg);

let assemblyFormat = "`<` params `>`";

let builders = [
TypeBuilder<(ins CArg<"ArrayRef<Type>", "{}">:$inputs, CArg<"ArrayRef<Type>", "{}">:$results, CArg<"bool", "false">:$isVarArg), [{
return Base::get($_ctxt, inputs, results, isVarArg);
TypeBuilder<(ins CArg<"ArrayRef<Type>", "{}">:$inputs, CArg<"ArrayRef<Type>", "{}">:$results, CArg<"bool", "false">:$isVarArg,
CArg<"bool", "false">:$isCVarArg), [{
return Base::get($_ctxt, inputs, results, isVarArg, isCVarArg);
}]>,
TypeBuilder<(ins "mlir::FunctionType":$funcType, CArg<"bool", "false">:$isVarArg), [{
return Base::get(funcType.getContext(), funcType.getInputs(), funcType.getResults(), isVarArg);
return Base::get(funcType.getContext(), funcType.getInputs(), funcType.getResults(), isVarArg, false);
}]>
];
let skipDefaultBuilders = 1;

let skipDefaultBuilders = 1;
//let genStorageClass = 0;
let extraClassDeclaration = [{
bool isVarArg() const;

/// A C variadic function (`@varargs`): the extra arguments of a call go to C's `...`.
bool isCVarArg() const { return getIsCVarArg(); }

/// Returns the result type of the function.
Type getReturnType();

Expand Down Expand Up @@ -826,24 +835,31 @@ def TypeScript_HybridFunction : TypeScript_Type<"HybridFunction"> {
let summary = "Map from a list of inputs to a list of results with/without 'this' reference";
let description = [{
}];
let parameters = (ins ArrayRefParameter<"Type", "type">:$inputs, ArrayRefParameter<"Type", "type">:$results, "bool":$isVarArg);
// `isCVarArg`: as for TypeScript_Function - a C variadic function, which a hybrid value holds
// with no `this`. Printed only when set.
let parameters = (ins ArrayRefParameter<"Type", "type">:$inputs, ArrayRefParameter<"Type", "type">:$results, "bool":$isVarArg,
DefaultValuedParameter<"bool", "false">:$isCVarArg);

let assemblyFormat = "`<` params `>`";

let builders = [
TypeBuilder<(ins "ArrayRef<Type>":$inputs, "ArrayRef<Type>":$results, "bool":$isVarArg), [{
return Base::get($_ctxt, inputs, results, isVarArg);
TypeBuilder<(ins "ArrayRef<Type>":$inputs, "ArrayRef<Type>":$results, "bool":$isVarArg, CArg<"bool", "false">:$isCVarArg), [{
return Base::get($_ctxt, inputs, results, isVarArg, isCVarArg);
}]>,
TypeBuilder<(ins "FunctionType":$funcType), [{
return Base::get(funcType.getContext(), funcType.getInputs(), funcType.getResults(), funcType.isVarArg());
return Base::get(funcType.getContext(), funcType.getInputs(), funcType.getResults(), funcType.isVarArg(),
funcType.isCVarArg());
}]>
];
];

let skipDefaultBuilders = 1;
let skipDefaultBuilders = 1;
//let genStorageClass = 0;
let extraClassDeclaration = [{
bool isVarArg() const;

/// A C variadic function (`@varargs`): the extra arguments of a call go to C's `...`.
bool isCVarArg() const { return getIsCVarArg(); }

/// Input types.
unsigned getNumInputs() const;
Type getInput(unsigned i) const { return getInputs()[i]; }
Expand Down
42 changes: 41 additions & 1 deletion tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2111,7 +2111,8 @@ struct SymbolCallInternalOpLowering : public TsLlvmPattern<mlir_ts::SymbolCallIn
else if (auto tsFuncOp = dyn_cast_or_null<mlir_ts::FuncOp>(funcOp))
{
auto tsFuncType = tsFuncOp.getFunctionType();
llvmFuncType = tch.convertFunctionSignature(op.getContext(), tsFuncType.getResults(), tsFuncType.getInputs(), tsFuncType.isVarArg());
llvmFuncType = tch.convertFunctionSignature(op.getContext(), tsFuncType.getResults(), tsFuncType.getInputs(),
tsFuncType.isVarArg() || tsFuncType.isCVarArg());
}

if (!llvmFuncType)
Expand Down Expand Up @@ -2383,6 +2384,39 @@ static mlir::Value recordFromRegisters(Operation *op, ConversionPatternRewriter
return rewriter.create<LLVM::LoadOp>(op->getLoc(), abi.recordResult, slot);
}

// An indirect call of a C variadic function (`@varargs`, the callee's !ts.func or
// !ts.hybrid_func says so) is a variadic call: `var_callee_type` names the fixed parameters, and
// the arguments after them go to `...` as the C ABI passes those (on Win64 a double goes in both
// its XMM and its integer register). Without it LLVM passes every argument as a fixed one, where
// printf does not look.
template <typename CallOpTy>
static void markCVarArgCall(CallOpTy callOp, mlir::Type calleeType, mlir::ValueRange args, mlir::TypeRange llvmResults)
{
auto fixedCount = 0u;
if (auto funcType = dyn_cast<mlir_ts::FunctionType>(calleeType); funcType && funcType.isCVarArg())
{
fixedCount = funcType.getNumInputs();
}
else if (auto hybridFuncType = dyn_cast<mlir_ts::HybridFunctionType>(calleeType); hybridFuncType && hybridFuncType.isCVarArg())
{
fixedCount = hybridFuncType.getNumInputs();
}
else
{
return;
}

SmallVector<mlir::Type> params;
for (auto arg : args.take_front(fixedCount))
{
params.push_back(arg.getType());
}

auto *context = callOp->getContext();
auto result = llvmResults.empty() ? LLVM::LLVMVoidType::get(context) : llvmResults.front();
callOp.setVarCalleeTypeAttr(mlir::TypeAttr::get(LLVM::LLVMFunctionType::get(result, params, /*isVarArg=*/true)));
}

struct CallInternalOpLowering : public TsLlvmPattern<mlir_ts::CallInternalOp>
{
using TsLlvmPattern<mlir_ts::CallInternalOp>::TsLlvmPattern;
Expand Down Expand Up @@ -2439,6 +2473,7 @@ struct CallInternalOpLowering : public TsLlvmPattern<mlir_ts::CallInternalOp>
// generic (TypeRange, ValueRange) builder above does not set it.
callRes.getProperties().setOperandSegmentSizes({static_cast<int32_t>(callOperands.size()), 0});
callRes.setOpBundleSizes({});
markCVarArgCall(callRes, op.getOperand(0).getType(), abi.args, llvmTypes);
if (abi.anyArgAttr)
{
callRes.setArgAttrsAttr(mlir::ArrayAttr::get(rewriter.getContext(), abi.argAttrs));
Expand Down Expand Up @@ -2549,6 +2584,8 @@ struct CallHybridInternalOpLowering : public TsLlvmPattern<mlir_ts::CallHybridIn
auto callRes = rewriter.create<LLVM::CallOp>(loc, llvmTypes, ops);
callRes.getProperties().setOperandSegmentSizes({static_cast<int32_t>(ops.size()), 0});
callRes.setOpBundleSizes({});
// a C variadic function is held with no `this`, so it is called here
markCVarArgCall(callRes, hybridFuncType, mlir::ValueRange(ops).drop_front(), llvmTypes);
return callRes.getResults();
});
}
Expand Down Expand Up @@ -2626,6 +2663,7 @@ struct InvokeOpLowering : public TsLlvmPattern<mlir_ts::InvokeOp>
auto invokeOp = rewriter.create<LLVM::InvokeOp>(op->getLoc(), llvmTypes, mlir::FlatSymbolRefAttr(), operands,
op.getNormalDest(), transformed.getNormalDestOperands(), op.getUnwindDest(),
transformed.getUnwindDestOperands());
markCVarArgCall(invokeOp, op.getCallOperands().front().getType(), abi.args, llvmTypes);
if (abi.anyArgAttr)
{
invokeOp.setArgAttrsAttr(mlir::ArrayAttr::get(rewriter.getContext(), abi.argAttrs));
Expand Down Expand Up @@ -2738,6 +2776,8 @@ struct InvokeHybridOpLowering : public TsLlvmPattern<mlir_ts::InvokeHybridOp>
auto callRes = rewriter.create<LLVM::InvokeOp>(loc, TypeRange(llvmTypes), mlir::FlatSymbolRefAttr(), ops, continuationBlock,
transformed.getNormalDestOperands(), op.getUnwindDest(),
transformed.getUnwindDestOperands());
// a C variadic function is held with no `this`, so it is called here
markCVarArgCall(callRes, hybridFuncType, mlir::ValueRange(ops).drop_front(), llvmTypes);

rewriter.setInsertionPointToStart(continuationBlock);

Expand Down
4 changes: 2 additions & 2 deletions tslang/lib/TypeScript/MLIRGenAccessCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2135,13 +2135,13 @@ namespace mlirgen
slotOperands.append(operands.begin(), operands.end());
callAttrs.push_back(builder.getNamedAttr(SRET_ARG_ATTR_NAME, builder.getI32IntegerAttr(0)));
callResult = mlirGenCallFunction(location, swappedFuncType, swappedFuncRefValue, slotOperands, genContext,
callAttrs);
callAttrs, /*hiddenOperandsCount=*/2);
}
else
{
slotOperands.append(operands.begin(), operands.end());
callResult = mlirGenCallFunction(location, calledFuncType, unboundFuncRefValue, thisValue, slotOperands, genContext,
callAttrs);
callAttrs, /*hiddenOperandsCount=*/1);
}

if (mlir::failed(callResult))
Expand Down
13 changes: 12 additions & 1 deletion tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1686,7 +1686,8 @@ namespace mlirgen
{
auto funcValue = builder.create<mlir_ts::CastOp>(
location,
mlir_ts::FunctionType::get(builder.getContext(), hybridFuncType.getInputs(), hybridFuncType.getResults(), hybridFuncType.isVarArg()),
mlir_ts::FunctionType::get(builder.getContext(), hybridFuncType.getInputs(), hybridFuncType.getResults(),
hybridFuncType.isVarArg(), hybridFuncType.isCVarArg()),
value);
return V(builder.create<mlir_ts::CastOp>(location, type, funcValue));
}
Expand Down Expand Up @@ -1748,6 +1749,16 @@ namespace mlirgen
EXIT_IF_FAILED(result);
}

// A C variadic function is called with C's variadic convention and a TypeScript
// function is not, so neither stands in for the other: through a value of the wrong
// kind the extra arguments would land in the wrong registers.
if (isCVarArgFunctionType(valueType) != isCVarArgFunctionType(type))
{
emitError(location, "can't assign '")
<< to_print(valueType) << "' to '" << to_print(type) << "': only one of them is a C variadic function";
return mlir::failure();
}

// fall through to finish cast operation
if (!mth.CanCastFunctionTypeToFunctionType(valueType, type))
{
Expand Down
11 changes: 11 additions & 0 deletions tslang/lib/TypeScript/MLIRGenFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,17 @@ namespace mlirgen
auto dllExport = processFunctionAttributes(location, fullName, functionLikeDeclarationBaseAST, attrs, funcProtoGenContext,
suppressExportForGenericInstantiation);

// A C variadic function (`@varargs`) says so in its type, so a call through a value of
// it - `let p = printf`, a `typeof printf` parameter - is a variadic call too. The type
// comes here from several places (discovered, cached, declared), so it is set here.
if (funcType && !funcType.isCVarArg() && llvm::any_of(attrs, [](mlir::NamedAttribute &attr) {
return attr.getName() == "func.varargs";
}))
{
funcType = mlir_ts::FunctionType::get(builder.getContext(), funcType.getInputs(), funcType.getResults(),
funcType.isVarArg(), /*isCVarArg=*/true);
}

if (funcType)
{
auto it = getCaptureVarsMap().find(funcProto->getName());
Expand Down
Loading
Loading