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
8 changes: 5 additions & 3 deletions tslang/include/TypeScript/TypeScriptOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2354,9 +2354,11 @@ def TypeScript_SetLengthOfOp : TypeScript_Op<"SetLengthOf"> {
let results = (outs );
}

def TypeScript_SetStringLengthOp : TypeScript_Op<"SetStringLength"> {
let arguments = (ins Arg<TypeScript_StringRef, "", [MemAlloc, MemWrite]>:$op, Index:$size);
let results = (outs );
// A new string of `size` bytes holding the start of `op` - `s.length = n`, whose result is then
// assigned to `s`. The source is left alone: whoever else holds it still does.
def TypeScript_StringResizeOp : TypeScript_Op<"StringResize"> {
let arguments = (ins TypeScript_String:$op, Index:$size);
let results = (outs Res<TypeScript_String, "", [MemAlloc]>:$result);
}

def TypeScript_StringLengthOp : TypeScript_Op<"StringLength"> {
Expand Down
2 changes: 1 addition & 1 deletion tslang/lib/TypeScript/LowerToAffineLoops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2657,7 +2657,7 @@ void AddTsAffineLegalOps(ConversionTarget &target)
mlir_ts::ElementRefOp, mlir_ts::PointerOffsetRefOp, mlir_ts::FuncOp, mlir_ts::GlobalOp, mlir_ts::GlobalResultOp, mlir_ts::DefaultOp,
mlir_ts::HasValueOp, mlir_ts::ValueOp, mlir_ts::ValueOrDefaultOp, mlir_ts::NullOp, mlir_ts::ParseFloatOp, mlir_ts::ParseIntOp, mlir_ts::IsNaNOp,
mlir_ts::PrintOp, mlir_ts::ConvertFOp, mlir_ts::SizeOfOp, mlir_ts::TypeDescriptorOp, mlir_ts::RetainOp, mlir_ts::ReleaseOp, mlir_ts::RetainSlotOp, mlir_ts::ReleaseSlotOp, mlir_ts::RetainCellOp, mlir_ts::ReleaseCellOp, mlir_ts::StoreOp, mlir_ts::SymbolRefOp, mlir_ts::LengthOfOp, mlir_ts::SetLengthOfOp,
mlir_ts::StringLengthOp, mlir_ts::SetStringLengthOp, mlir_ts::StringConcatOp, mlir_ts::StringCompareOp, mlir_ts::AnyCompareOp,
mlir_ts::StringLengthOp, mlir_ts::StringResizeOp, mlir_ts::StringConcatOp,mlir_ts::StringCompareOp, mlir_ts::AnyCompareOp,
mlir_ts::LoadOp, mlir_ts::LoadSaveOp, mlir_ts::NewOp, mlir_ts::CreateTupleOp, mlir_ts::DeconstructTupleOp, mlir_ts::CreateArrayOp,
mlir_ts::NewEmptyArrayOp, mlir_ts::NewArrayOp, mlir_ts::DeleteOp, mlir_ts::PropertyRefOp, mlir_ts::InsertPropertyOp,
mlir_ts::ExtractPropertyOp, mlir_ts::LogicalBinaryOp, mlir_ts::UndefOp, mlir_ts::VariableOp, mlir_ts::DebugVariableOp, mlir_ts::AllocaOp,
Expand Down
118 changes: 64 additions & 54 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,47 +666,45 @@ class SetLengthOfOpLowering : public TsLlvmPattern<mlir_ts::SetLengthOfOp>

rewriter.create<LLVM::StoreOp>(loc, allocated, currentPtrPtr);

// `arr.length = n` on a grown array exposes slots the allocator has not written, and a
// store into one gives up what the slot held first (`isOwnedElementSlot`) - so the
// release reads whatever was last in that memory. `result.length = this.length` followed
// by `result[i] = ..` is exactly the default library's `Array.map`, which is why
// `arrS.map(e => e + "_")` crashed about one run in three (§9.37).
// `arr.length = n` on a grown array exposes slots the allocator has not written. They read
// as zero: the default library's Set and Map grow their `int[]` buckets this way and take
// an unwritten bucket for an empty one, and under rc and own a store into a slot gives up
// what the slot held first (`isOwnedElementSlot`), so the release would read whatever was
// last in that memory - `result.length = this.length` followed by `result[i] = ..` is the
// default library's `Array.map`, and `arrS.map(e => e + "_")` crashed about one run in
// three (§9.37).
//
// Only where an element owns something, and only under -mm=rc: nothing reads an
// unwritten slot in the other models, and the memset is not free.
if (tsLlvmContext->compileOptions.tracksOwnership())
{
MLIRTypeHelper mth(rewriter.getContext(), tsLlvmContext->compileOptions);
if (mth.ownsHeapMemory(loc, elementType))
{
auto oldBytes = rewriter.create<mlir::index::MulOp>(loc, th.getIndexType(),
ValueRange{sizeOfTypeAsIndexType,
rewriter.create<mlir::index::CastUOp>(
loc, th.getIndexType(), countAsIndexType)});
auto grew = rewriter.create<mlir::index::CmpOp>(loc, mlir::index::IndexCmpPredicate::UGT,
multSizeOfTypeValue, oldBytes);

auto *currentBlock = rewriter.getInsertionBlock();
auto *continuationBlock = rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
auto *zeroBlock = rewriter.createBlock(continuationBlock);

rewriter.setInsertionPointToEnd(zeroBlock);
auto tailStart = rewriter.create<LLVM::GEPOp>(loc, ptrType, th.getI8Type(), allocated,
ValueRange{rewriter.create<mlir::index::CastUOp>(
loc, llvmIndexType, oldBytes)});
auto tailBytes = rewriter.create<mlir::index::SubOp>(loc, th.getIndexType(),
multSizeOfTypeValue, oldBytes);
rewriter.create<LLVM::MemsetOp>(
loc, tailStart,
rewriter.create<LLVM::ConstantOp>(loc, th.getI8Type(), rewriter.getI8IntegerAttr(0)),
rewriter.create<mlir::index::CastUOp>(loc, llvmIndexType, tailBytes), /*isVolatile=*/false);
rewriter.create<LLVM::BrOp>(loc, ValueRange{}, continuationBlock);

rewriter.setInsertionPointToEnd(currentBlock);
rewriter.create<LLVM::CondBrOp>(loc, grew, zeroBlock, continuationBlock);

rewriter.setInsertionPointToStart(continuationBlock);
}
// Every model but gc, whose collector hands back cleared memory - also when a block
// shrunk in place grows again, GC_realloc having cleared what the shrink gave up.
if (!tsLlvmContext->compileOptions.needsGCRuntime())
{
auto oldBytes = rewriter.create<mlir::index::MulOp>(loc, th.getIndexType(),
ValueRange{sizeOfTypeAsIndexType,
rewriter.create<mlir::index::CastUOp>(
loc, th.getIndexType(), countAsIndexType)});
auto grew = rewriter.create<mlir::index::CmpOp>(loc, mlir::index::IndexCmpPredicate::UGT,
multSizeOfTypeValue, oldBytes);

auto *currentBlock = rewriter.getInsertionBlock();
auto *continuationBlock = rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
auto *zeroBlock = rewriter.createBlock(continuationBlock);

rewriter.setInsertionPointToEnd(zeroBlock);
auto tailStart = rewriter.create<LLVM::GEPOp>(loc, ptrType, th.getI8Type(), allocated,
ValueRange{rewriter.create<mlir::index::CastUOp>(
loc, llvmIndexType, oldBytes)});
auto tailBytes = rewriter.create<mlir::index::SubOp>(loc, th.getIndexType(),
multSizeOfTypeValue, oldBytes);
rewriter.create<LLVM::MemsetOp>(
loc, tailStart,
rewriter.create<LLVM::ConstantOp>(loc, th.getI8Type(), rewriter.getI8IntegerAttr(0)),
rewriter.create<mlir::index::CastUOp>(loc, llvmIndexType, tailBytes), /*isVolatile=*/false);
rewriter.create<LLVM::BrOp>(loc, ValueRange{}, continuationBlock);

rewriter.setInsertionPointToEnd(currentBlock);
rewriter.create<LLVM::CondBrOp>(loc, grew, zeroBlock, continuationBlock);

rewriter.setInsertionPointToStart(continuationBlock);
}

auto newCountAsLLVMType = rewriter.create<mlir::index::CastUOp>(loc, llvmIndexType, newCountAsIndexType);
Expand Down Expand Up @@ -743,34 +741,46 @@ class StringLengthOpLowering : public TsLlvmPattern<mlir_ts::StringLengthOp>
}
};

class SetStringLengthOpLowering : public TsLlvmPattern<mlir_ts::SetStringLengthOp>
// A new block of `size` bytes with as much of the source as fits, its terminator included; the
// source is read and nothing else. A null source is copied as the empty string.
class StringResizeOpLowering : public TsLlvmPattern<mlir_ts::StringResizeOp>
{
public:
using TsLlvmPattern<mlir_ts::SetStringLengthOp>::TsLlvmPattern;
using TsLlvmPattern<mlir_ts::StringResizeOp>::TsLlvmPattern;

LogicalResult matchAndRewrite(mlir_ts::SetStringLengthOp op, Adaptor transformed,
LogicalResult matchAndRewrite(mlir_ts::StringResizeOp op, Adaptor transformed,
ConversionPatternRewriter &rewriter) const final
{
TypeHelper th(rewriter);
CodeLogicHelper clh(op, rewriter);
LLVMCodeHelper ch(op, rewriter, getTypeConverter(), tsLlvmContext->compileOptions);
TypeConverterHelper tch(getTypeConverter());

auto loc = op->getLoc();

// TODO implement str concat
auto i8PtrTy = th.getPtrType();
auto llvmIndexType = tch.convertType(th.getIndexType());

mlir::Value ptr = transformed.getOp();
mlir::Value size = transformed.getSize();
auto strType = mlir_ts::StringType::get(rewriter.getContext());
mlir::Value source = transformed.getOp();
mlir::Value emptyText = rewriter.create<mlir_ts::DialectCastOp>(
loc, tch.convertType(strType), rewriter.create<mlir_ts::ConstantOp>(loc, strType, rewriter.getStringAttr("")));
auto isNull = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::eq, source,
rewriter.create<LLVM::ZeroOp>(loc, i8PtrTy));
source = rewriter.create<LLVM::SelectOp>(loc, isNull, emptyText, source);

mlir::Value strPtr = rewriter.create<LLVM::LoadOp>(loc, i8PtrTy, ptr);
mlir::Value size = transformed.getSize();
auto newStringValue = ch.MemoryAlloc(size);

mlir::Value newStringValue = ch.MemoryRealloc(strPtr, size);
auto strlenFuncOp = ch.getOrInsertFunction("strlen", th.getFunctionType(llvmIndexType, {i8PtrTy}));
mlir::Value sourceBytes = rewriter.create<LLVM::CallOp>(loc, strlenFuncOp, ValueRange{source}).getResult();
sourceBytes = rewriter.create<LLVM::AddOp>(
loc, llvmIndexType,
ValueRange{sourceBytes, rewriter.create<LLVM::ConstantOp>(loc, llvmIndexType, rewriter.getIntegerAttr(llvmIndexType, 1))});
auto fits = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::ult, sourceBytes, size);
auto copyBytes = rewriter.create<LLVM::SelectOp>(loc, fits, sourceBytes, size);

rewriter.create<LLVM::StoreOp>(loc, newStringValue, ptr);
rewriter.eraseOp(op);
rewriter.create<LLVM::MemcpyOp>(loc, newStringValue, source, copyBytes, /*isVolatile=*/false);

rewriter.replaceOp(op, ValueRange{newStringValue});
return success();
}
};
Expand Down Expand Up @@ -4171,7 +4181,7 @@ struct GlobalOpLowering : public TsLlvmPattern<mlir_ts::GlobalOp>
isa<mlir_ts::LoadLibraryPermanentlyOp>(op) || isa<mlir_ts::SearchForAddressOfSymbolOp>(op) ||
isa<mlir_ts::ArrayPushOp>(op) || isa<mlir_ts::ArrayUnshiftOp>(op) || isa<mlir_ts::ArraySpliceOp>(op) ||
isa<mlir_ts::ArrayPopOp>(op) || isa<mlir_ts::ArrayShiftOp>(op) || isa<mlir_ts::DeleteOp>(op) ||
isa<mlir_ts::SetLengthOfOp>(op) || isa<mlir_ts::SetStringLengthOp>(op) ||
isa<mlir_ts::SetLengthOfOp>(op) || isa<mlir_ts::StringResizeOp>(op) ||
isa<mlir_ts::StringConcatOp>(op) || isa<mlir_ts::CharToStringOp>(op) ||
// a tagged union's value goes in and out through memory (an alloca and a copy),
// which a global's initializer cannot hold
Expand Down Expand Up @@ -7819,7 +7829,7 @@ void TypeScriptToLLVMLoweringPass::runOnOperation()
DeconstructTupleOpLowering, CreateArrayOpLowering, NewEmptyArrayOpLowering, NewArrayOpLowering, ArrayPushOpLowering,
ArrayPopOpLowering, ArrayUnshiftOpLowering, ArrayShiftOpLowering, ArraySpliceOpLowering, ArrayViewOpLowering, DeleteOpLowering,
ParseFloatOpLowering, ParseIntOpLowering, IsNaNOpLowering, PrintOpLowering, ConvertFOpLowering, StoreOpLowering, SizeOfOpLowering, TypeDescriptorOpLowering, RetainOpLowering, ReleaseOpLowering, RetainSlotOpLowering, ReleaseSlotOpLowering, RetainCellOpLowering, ReleaseCellOpLowering,
InsertPropertyOpLowering, LengthOfOpLowering, SetLengthOfOpLowering, StringLengthOpLowering, SetStringLengthOpLowering, StringConcatOpLowering,
InsertPropertyOpLowering, LengthOfOpLowering, SetLengthOfOpLowering, StringLengthOpLowering, StringResizeOpLowering, StringConcatOpLowering,
StringCompareOpLowering, AnyCompareOpLowering, CharToStringOpLowering, UndefOpLowering, CopyStructOpLowering, MemoryCopyOpLowering, MemoryMoveOpLowering,
LoadSaveValueLowering, ThrowUnwindOpLowering, ThrowCallOpLowering, VariableOpLowering, DebugVariableOpLowering, AllocaOpLowering, InvokeOpLowering,
InvokeHybridOpLowering, VirtualSymbolRefOpLowering, ThisVirtualSymbolRefOpLowering, InterfaceSymbolRefOpLowering,
Expand Down
33 changes: 22 additions & 11 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5525,17 +5525,18 @@ class MLIRGenImpl
}
else if (auto stringLength = leftExpressionValueBeforeCast.getDefiningOp<mlir_ts::StringLengthOp>())
{
MLIRCodeLogic mcl(builder, compileOptions);
auto stringValueLoaded = mcl.GetReferenceFromValue(location, stringLength.getOp());
if (!stringValueLoaded)
{
emitError(location) << "Can't get reference of the string, ensure const string is not used";
return mlir::failure();
}

// special case to resize array
// A string is a value: resizing one makes a new string, copied at its new size and
// assigned as `s = s + t` would be, and whoever else held the old one still does - the
// caller whose string a `this: string` parameter borrowed, a temporary, another
// variable. Resizing in place reallocated the block, which freed it when it moved
// under every model but gc, and under rc the holder then released it again.
syncSavingValue(stringLength.getResult().getType());
builder.create<mlir_ts::SetStringLengthOp>(location, stringValueLoaded, savingValue);
auto resized = builder.create<mlir_ts::StringResizeOp>(location, getStringType(), stringLength.getOp(),
savingValue);
markFreshBlockOwned(location, resized);

auto saved = mlirGenSaveLogicOneItem(location, stringLength.getOp(), resized, genContext);
EXIT_IF_FAILED(saved)
}
else
{
Expand Down Expand Up @@ -7485,8 +7486,18 @@ class MLIRGenImpl

operands.pop_back_n(toIndex - fromIndex);

auto packed = !varArgOperands.empty() || isa<mlir_ts::ArrayType>(varArgsType);

// the data block about to be filled releases every element when it dies; without
// this, `a.concat(["x"])` handed the callee an array nobody held, and its first
// `for (const item of other)` freed it (§9.21, as for an array literal)
if (packed)
{
mlirGenRetainCaptured(location, varArgOperands);
}

// create array
auto array = varArgOperands.empty() && !isa<mlir_ts::ArrayType>(varArgsType)
auto array = !packed
? V(builder.create<mlir_ts::UndefOp>(location, varArgsType))
: V(builder.create<mlir_ts::CreateArrayOp>(location, varArgsType, varArgOperands));
operands.push_back(array);
Expand Down
2 changes: 1 addition & 1 deletion tslang/lib/TypeScript/OwnershipFacts.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ inline bool isFresh(mlir::Value value)
}

if (mlir::isa<mlir_ts::NewOp, mlir_ts::CreateArrayOp, mlir_ts::NewArrayOp, mlir_ts::StringConcatOp,
mlir_ts::CharToStringOp>(def))
mlir_ts::StringResizeOp, mlir_ts::CharToStringOp>(def))
{
return true;
}
Expand Down
3 changes: 2 additions & 1 deletion tslang/lib/TypeScript/OwnershipInferencePass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1446,7 +1446,8 @@ class OwnershipInferencePass : public mlir::PassWrapper<OwnershipInferencePass,
if (mlir::isa<mlir_ts::PrintOp, mlir_ts::PropertyRefOp, mlir_ts::ElementRefOp, mlir_ts::LengthOfOp,
mlir_ts::ThisSymbolRefOp, mlir_ts::VirtualSymbolRefOp, mlir_ts::ThisVirtualSymbolRefOp,
mlir_ts::InterfaceSymbolRefOp, mlir_ts::GetThisOp, mlir_ts::GetMethodOp,
mlir_ts::ArithmeticBinaryOp, mlir_ts::LogicalBinaryOp, mlir_ts::StringConcatOp>(user))
mlir_ts::ArithmeticBinaryOp, mlir_ts::LogicalBinaryOp, mlir_ts::StringConcatOp,
mlir_ts::StringResizeOp>(user))
{
return true;
}
Expand Down
Loading
Loading