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
10 changes: 5 additions & 5 deletions tslang/include/TypeScript/TypeScriptOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2302,13 +2302,13 @@ def TypeScript_BodyResultInternalOp : TypeScript_YieldOpBase<"BodyResultInternal
let summary = "termination operation";
}

def TypeScript_ArrayPushOp : TypeScript_Op<"ArrayPush", [
RangedTypesMatchWith<"array element type match all items types",
"op", "items",
"TypeRange((cast<ArrayType>(cast<RefType>($_self).getElementType()).getElementType()))">
]> {
def TypeScript_ArrayPushOp : TypeScript_Op<"ArrayPush"> {
let arguments = (ins Arg<TypeScript_AnyArrayRef, "", [MemAlloc, MemWrite]>:$op, Variadic<AnyType>:$items);
let results = (outs Index:$new_size);

// every item is of the element type (a RangedTypesMatchWith against a one-type range
// accepted only a single item)
let hasVerifier = 1;
}

def TypeScript_ArrayPopOp : TypeScript_Op<"ArrayPop", [
Expand Down
192 changes: 191 additions & 1 deletion tslang/lib/TypeScript/MLIRGenAccessCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1365,10 +1365,200 @@ namespace mlirgen
functionName,
typeArgs,
operands,
[this](mlir::Location location, mlir::Type type, mlir::Value value, const GenContext &genContext, bool disableStrictNullCheck) { return cast(location, type, value, genContext, disableStrictNullCheck); },
[this](mlir::Location location, mlir::Type type, mlir::Value value, const GenContext &genContext, bool disableStrictNullCheck) { return cast(location, type, value, genContext, disableStrictNullCheck); },
genContext);
}

// `a.push(...xs)`, `a.unshift(...xs)`, `a.splice(start, count, ...xs)`. The builtins take one
// operand per item, and a spread, whose length is known only at run time, has none to give:
// it reached ArrayPush as the array itself and failed to verify. Every item argument, spread
// or not, goes into one array literal instead (a literal spreads at run time), and its elements
// go in one at a time through the same builtin with no spread, so the casts and the retains
// stay the builtin's.
std::optional<ValueOrLogicalResult> MLIRGenImpl::mlirGenArrayInsertWithSpread(
mlir::Location location, mlir::Value funcResult, NodeArray<Expression> arguments, const GenContext &genContext)
{
auto thisSymbolRefOp = funcResult.getDefiningOp<mlir_ts::ThisSymbolRefOp>();
if (!thisSymbolRefOp || !mth.isBuiltinFunctionType(funcResult)
|| llvm::none_of(arguments, [](auto argument) { return argument == SyntaxKind::SpreadElement; }))
{
return std::nullopt;
}

auto calleeName = thisSymbolRefOp->getAttrOfType<mlir::FlatSymbolRefAttr>(StringRef(IDENTIFIER_ATTR_NAME));
auto functionName = calleeName ? calleeName.getValue() : StringRef();
auto isPush = functionName == "__array_push";
auto isSplice = functionName == "__array_splice";
if (!isPush && !isSplice && functionName != "__array_unshift")
{
return std::nullopt;
}

auto thisValue = thisSymbolRefOp.getThisVal();
auto arrayType = dyn_cast<mlir_ts::ArrayType>(thisValue.getType());
if (!arrayType)
{
return std::nullopt;
}

auto generate = [&]() -> ValueOrLogicalResult {
MLIRCodeLogic mcl(builder, compileOptions);
auto arrayRef = mcl.GetReferenceFromValue(location, thisValue);
if (!arrayRef)
{
emitError(location) << "Can't get reference of the array, ensure const array is not used";
return mlir::failure();
}

// splice's start and delete count are positions, not items, and come first
auto itemsFrom = isSplice ? 2 : 0;
SmallVector<mlir::Value, 2> positions;
for (auto index = 0; index < itemsFrom; index++)
{
if (index >= (int)arguments.size() || arguments[index] == SyntaxKind::SpreadElement)
{
emitError(location) << "splice with spread items needs its start and delete count as arguments of their own";
return mlir::failure();
}

auto result = mlirGen(arguments[index], genContext);
EXIT_IF_FAILED_OR_NO_VALUE(result)
auto value = V(result);
CAST(value, location, getNumberType(), value, genContext);
positions.push_back(value);
}

NodeFactory nf(NodeFactoryFlags::None);

NodeArray<Expression> itemArguments;
for (auto index = itemsFrom; index < (int)arguments.size(); index++)
{
itemArguments.push_back(arguments[index]);
}

GenContext itemsGenContext(genContext);
itemsGenContext.clearReceiverTypes();
itemsGenContext.receiverType = arrayType;
auto itemsResult = mlirGen(nf.createArrayLiteralExpression(itemArguments), itemsGenContext);
EXIT_IF_FAILED_OR_NO_VALUE(itemsResult)
auto items = V(itemsResult);
if (items.getType() != arrayType)
{
CAST(items, location, arrayType, items, genContext);
}

if (isPush)
{
if (mlir::failed(mlirGenAppendArrayByEachElement(location, arrayRef, items, genContext)))
{
return mlir::failure();
}
}
else
{
SymbolTableScopeT varScope(symbolTable);

auto dstArrayVarDecl = std::make_shared<VariableDeclarationDOM>(".dst_array", arrayType, location);
dstArrayVarDecl->setReadWriteAccess(true);
DECLARE(dstArrayVarDecl, arrayRef);

auto itemsVarDecl = std::make_shared<VariableDeclarationDOM>(".items", arrayType, location);
DECLARE(itemsVarDecl, items);

auto _dst_array = nf.createIdentifier(S(".dst_array"));
auto _items = nf.createIdentifier(S(".items"));
auto _at = nf.createIdentifier(S(".at"));
auto _i = nf.createIdentifier(S(".i"));
auto _length = [&](Identifier array) {
return nf.createPropertyAccessExpression(array, nf.createIdentifier(S(LENGTH_FIELD_NAME)));
};
auto _splice = [&](Expression start, Expression count, Expression item) {
NodeArray<Expression> spliceArguments;
spliceArguments.push_back(start);
spliceArguments.push_back(count);
if (item)
{
spliceArguments.push_back(item);
}

return nf.createExpressionStatement(nf.createCallExpression(
nf.createPropertyAccessExpression(_dst_array, nf.createIdentifier(S("splice"))), undefined, spliceArguments));
};
auto _assign = [&](Expression value) {
return nf.createExpressionStatement(nf.createBinaryExpression(_at, nf.createToken(SyntaxKind::EqualsToken), value));
};
auto _zero = [&]() { return nf.createNumericLiteral(S("0")); };

// let .at = 0 (unshift), or splice's start where JavaScript puts it: a negative one
// counts from the end and stops at 0, one past the end is the end
NodeArray<VariableDeclaration> atDeclarations;
if (isSplice)
{
auto startVarDecl = std::make_shared<VariableDeclarationDOM>(".start", getNumberType(), location);
DECLARE(startVarDecl, positions[0]);
auto countVarDecl = std::make_shared<VariableDeclarationDOM>(".count", getNumberType(), location);
DECLARE(countVarDecl, positions[1]);

auto _start = nf.createIdentifier(S(".start"));
atDeclarations.push_back(nf.createVariableDeclaration(_at, undefined, undefined, _start));
if (mlir::failed(mlirGen(nf.createVariableStatement(undefined, nf.createVariableDeclarationList(atDeclarations, NodeFlags::Let)), genContext)))
{
return mlir::failure();
}

// if (.at < 0) { .at = .at + .dst_array.length; if (.at < 0) .at = 0; }
// else if (.at > .dst_array.length) .at = .dst_array.length;
NodeArray<Statement> fromEnd;
fromEnd.push_back(_assign(nf.createBinaryExpression(_at, nf.createToken(SyntaxKind::PlusToken), _length(_dst_array))));
fromEnd.push_back(nf.createIfStatement(
nf.createBinaryExpression(_at, nf.createToken(SyntaxKind::LessThanToken), _zero()), _assign(_zero())));
auto normalize = nf.createIfStatement(
nf.createBinaryExpression(_at, nf.createToken(SyntaxKind::LessThanToken), _zero()),
nf.createBlock(fromEnd),
nf.createIfStatement(
nf.createBinaryExpression(_at, nf.createToken(SyntaxKind::GreaterThanToken), _length(_dst_array)),
_assign(_length(_dst_array))));
if (mlir::failed(mlirGen(normalize, genContext)))
{
return mlir::failure();
}

// the delete runs first; everything before .at stays where it is
if (mlir::failed(mlirGen(_splice(_start, nf.createIdentifier(S(".count")), undefined), genContext)))
{
return mlir::failure();
}
}
else
{
atDeclarations.push_back(nf.createVariableDeclaration(_at, undefined, undefined, _zero()));
if (mlir::failed(mlirGen(nf.createVariableStatement(undefined, nf.createVariableDeclarationList(atDeclarations, NodeFlags::Let)), genContext)))
{
return mlir::failure();
}
}

// for (let .i = 0; .i < .items.length; ++.i) .dst_array.splice(.at + .i, 0, .items[.i]);
NodeArray<VariableDeclaration> declarations;
declarations.push_back(nf.createVariableDeclaration(_i, undefined, undefined, _zero()));
auto initVars = nf.createVariableDeclarationList(declarations, NodeFlags::Let);
auto cond = nf.createBinaryExpression(_i, nf.createToken(SyntaxKind::LessThanToken), _length(_items));
auto incr = nf.createPrefixUnaryExpression(nf.createToken(SyntaxKind::PlusPlusToken), _i);
auto insert = _splice(
nf.createBinaryExpression(_at, nf.createToken(SyntaxKind::PlusToken), _i), _zero(), nf.createElementAccessExpression(_items, _i));
if (mlir::failed(mlirGen(nf.createForStatement(initVars, cond, incr, insert), genContext)))
{
return mlir::failure();
}
}

auto loadedArray = builder.create<mlir_ts::LoadOp>(location, arrayType, arrayRef);
return V(builder.create<mlir_ts::LengthOfOp>(location, builder.getIndexType(), loadedArray));
};

return generate();
}

ValueOrLogicalResult MLIRGenImpl::mlirGenCallExpression(mlir::Location location, mlir::Value funcResult,
NodeArray<TypeNode> typeArguments, SmallVector<mlir::Value, 4> &operands,
const GenContext &genContext)
Expand Down
5 changes: 5 additions & 0 deletions tslang/lib/TypeScript/MLIRGenExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,11 @@ namespace mlirgen
mth.isGenericType(funcResult.getType())
&& callExpression->typeArguments.size() == 0;

if (auto spreadResult = mlirGenArrayInsertWithSpread(location, funcResult, callExpression->arguments, genContext))
{
return *spreadResult;
}

SmallVector<mlir::Value, 4> operands;
auto offsetArgs = isa<mlir_ts::BoundFunctionType>(funcType) || isa<mlir_ts::ExtensionFunctionType>(funcType) ? 1 : 0;
if (mlir::failed(mlirGenOperands(callExpression->arguments, operands, funcResult.getType(), genContext, offsetArgs, noReceiverTypesForGenericCall)))
Expand Down
5 changes: 4 additions & 1 deletion tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -7337,9 +7337,12 @@ class MLIRGenImpl
const GenContext &genContext);

ValueOrLogicalResult mlirGenCallBuiltInFunction(
mlir::Location location, mlir::Value actualFuncRefValue, NodeArray<TypeNode> typeArguments,
mlir::Location location, mlir::Value actualFuncRefValue, NodeArray<TypeNode> typeArguments,
SmallVector<mlir::Value, 4> &operands, const GenContext &genContext);

std::optional<ValueOrLogicalResult> mlirGenArrayInsertWithSpread(
mlir::Location location, mlir::Value funcResult, NodeArray<Expression> arguments, const GenContext &genContext);

ValueOrLogicalResult mlirGenCallExpression(mlir::Location location, mlir::Value funcResult,
NodeArray<TypeNode> typeArguments, SmallVector<mlir::Value, 4> &operands,
const GenContext &genContext);
Expand Down
18 changes: 18 additions & 0 deletions tslang/lib/TypeScript/TypeScriptOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,24 @@ LogicalResult mlir_ts::FuncOp::verify()
return success();
}

//===----------------------------------------------------------------------===//
// ArrayPushOp
//===----------------------------------------------------------------------===//

LogicalResult mlir_ts::ArrayPushOp::verify()
{
auto elementType = mlir::cast<mlir_ts::ArrayType>(mlir::cast<mlir_ts::RefType>(getOp().getType()).getElementType()).getElementType();
for (auto item : getItems())
{
if (item.getType() != elementType)
{
return emitOpError("item of type ") << item.getType() << " does not match array element type " << elementType;
}
}

return success();
}

//===----------------------------------------------------------------------===//
// InvokeOp
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ tslang_add_test(NAME test-compile-00-arrays4-push-pop COMMAND test-runner "${PRO
tslang_add_test(NAME test-compile-00-array-shift COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_shift.ts")
tslang_add_test(NAME test-compile-00-array-splice COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_splice.ts")
tslang_add_test(NAME test-compile-00-const-array-push COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_array_push.ts")
tslang_add_test(NAME test-compile-00-array-spread-args COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_spread_args.ts")
tslang_add_test(NAME test-compile-00-arrays5-deconstruct COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array5_deconst.ts")
tslang_add_test(NAME test-compile-00-arrays6 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array6.ts")
tslang_add_test(NAME test-compile-00-arrays7 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00array7.ts")
Expand Down Expand Up @@ -787,6 +788,7 @@ tslang_add_test(NAME test-jit-00-arrays4-push-pop COMMAND test-runner -jit "${PR
tslang_add_test(NAME test-jit-00-array-shift COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_shift.ts")
tslang_add_test(NAME test-jit-00-array-splice COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_splice.ts")
tslang_add_test(NAME test-jit-00-const-array-push COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00const_array_push.ts")
tslang_add_test(NAME test-jit-00-array-spread-args COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array_spread_args.ts")
tslang_add_test(NAME test-jit-00-arrays5-deconstruct COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array5_deconst.ts")
tslang_add_test(NAME test-jit-00-arrays6 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array6.ts")
tslang_add_test(NAME test-jit-00-arrays7 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00array7.ts")
Expand Down Expand Up @@ -1551,6 +1553,7 @@ set(TSLANG_CORPUS
00array_of.ts
00array_shift.ts
00array_splice.ts
00array_spread_args.ts
00array_view.ts
00const_array_push.ts
00array.ts
Expand Down
63 changes: 63 additions & 0 deletions tslang/test/tester/tests/00array_spread_args.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// push/unshift/splice with spread arguments, whose count is known only at run time, and push with
// more than one item (the op's verifier accepted exactly one).
function same(a: number[], expected: number[]) {
if (a.length != expected.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] != expected[i]) return false;
}

return true;
}

function spreadOf(values: number[]) {
let a: number[] = [1, 2];
a.push(...values);
return a;
}

function main() {
let m: number[] = [];
assert(m.push(1, 2) == 2, "push two: result");
assert(same(m, [1, 2]), "push two");

const extra = [3, 4, 5];

let a: number[] = [1, 2];
assert(a.push(...extra) == 5, "push spread: result");
assert(same(a, [1, 2, 3, 4, 5]), "push spread");

a.push(0, ...extra, 9);
assert(same(a, [1, 2, 3, 4, 5, 0, 3, 4, 5, 9]), "push mixed");

assert(same(spreadOf([]), [1, 2]), "push empty spread");
assert(same(spreadOf([7, 8]), [1, 2, 7, 8]), "push parameter spread");

let b: number[] = [9];
b.unshift(...extra);
assert(same(b, [3, 4, 5, 9]), "unshift spread");
b.unshift(7, ...[8]);
assert(same(b, [7, 8, 3, 4, 5, 9]), "unshift mixed");

let c: number[] = [1, 2, 3];
c.splice(1, 1, ...extra);
assert(same(c, [1, 3, 4, 5, 3]), "splice spread");
c.splice(-1, 1, ...[10, 11]);
assert(same(c, [1, 3, 4, 5, 10, 11]), "splice negative start");
c.splice(100, 0, ...[12]);
assert(same(c, [1, 3, 4, 5, 10, 11, 12]), "splice start past the end");
c.splice(-100, 2, ...[0]);
assert(same(c, [0, 4, 5, 10, 11, 12]), "splice start before the beginning");

const ints = [1, 2];
let nums: number[] = [];
nums.push(...ints);
assert(same(nums, [1, 2]), "push s32 spread into number[]");

let ss: string[] = [];
ss.push(...["a"], "b");
assert(ss.length == 2, "strings: length");
assert(ss[0] == "a", "strings: first");
assert(ss[1] == "b", "strings: second");

print("done.");
}
Loading