diff --git a/lib/Conversion/MooreToCore/MooreToCore.cpp b/lib/Conversion/MooreToCore/MooreToCore.cpp index ec894143a1e1..1f7de8515793 100644 --- a/lib/Conversion/MooreToCore/MooreToCore.cpp +++ b/lib/Conversion/MooreToCore/MooreToCore.cpp @@ -60,7 +60,7 @@ struct ClassTypeCache { /// Record/overwrite the field path to a single property for a class. void setFieldPath(StringRef propertyName, ArrayRef path) { this->propertyPath[propertyName] = - llvm::SmallVector(path.begin(), path.end()); + SmallVector(path.begin(), path.end()); } /// Lookup the full GEP path for a (class, field). @@ -94,6 +94,24 @@ struct ClassTypeCache { DenseMap classToStructMap; }; +/// Ensure we have `declare i8* @malloc(i64)` (opaque ptr prints as !llvm.ptr). +static LLVM::LLVMFuncOp getOrCreateMalloc(ModuleOp mod, OpBuilder &b) { + if (auto f = mod.lookupSymbol("malloc")) + return f; + + OpBuilder::InsertionGuard g(b); + b.setInsertionPointToStart(mod.getBody()); + + auto i64Ty = IntegerType::get(mod.getContext(), 64); + auto ptrTy = LLVM::LLVMPointerType::get(mod.getContext()); // opaque pointer + auto fnTy = LLVM::LLVMFunctionType::get(ptrTy, {i64Ty}, false); + + auto fn = LLVM::LLVMFuncOp::create(b, mod.getLoc(), "malloc", fnTy); + // Link this in from somewhere else. + fn.setLinkage(LLVM::Linkage::External); + return fn; +} + /// Helper function to create an opaque LLVM Struct Type which corresponds /// to the sym static LLVM::LLVMStructType getOrCreateOpaqueStruct(MLIRContext *ctx, @@ -120,12 +138,15 @@ static LogicalResult resolveClassStructBody(ClassDeclOp op, if (auto baseClass = op.getBaseAttr()) { + ModuleOp mod = op->getParentOfType(); + auto *opSym = mod.lookupSymbol(baseClass); + auto classDeclOp = cast(opSym); + + if (failed(resolveClassStructBody(classDeclOp, typeConverter, cache))) + return failure(); + // Process base class' struct layout first auto baseClassStruct = cache.getStructInfo(baseClass); - if (!baseClassStruct) - return op.emitOpError() << "Base class " << baseClass << " of " - << classSym << " has not been converted."; - structBodyMembers.push_back(baseClassStruct->classBody); derivedStartIdx = 1; @@ -171,6 +192,14 @@ static LogicalResult resolveClassStructBody(ClassDeclOp op, return success(); } +/// Convenience overload that looks up ClassDeclOp +static LogicalResult resolveClassStructBody(ModuleOp mod, SymbolRefAttr op, + TypeConverter const &typeConverter, + ClassTypeCache &cache) { + auto classDeclOp = cast(*mod.lookupSymbol(op)); + return resolveClassStructBody(classDeclOp, typeConverter, cache); +} + /// Returns the passed value if the integer width is already correct. /// Zero-extends if it is too narrow. /// Truncates if the integer is too wide and the truncated part is zero, if it @@ -678,6 +707,52 @@ static Value createZeroValue(Type type, Location loc, return rewriter.createOrFold(loc, type, constZero); } +/// moore.class.new lowering: heap-allocate storage for the class object. +struct ClassNewOpConversion : public OpConversionPattern { + ClassNewOpConversion(TypeConverter &tc, MLIRContext *ctx, + ClassTypeCache &cache) + : OpConversionPattern(tc, ctx), cache(cache) {} + + LogicalResult + matchAndRewrite(ClassNewOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + Location loc = op.getLoc(); + MLIRContext *ctx = rewriter.getContext(); + + auto handleTy = cast(op.getResult().getType()); + auto sym = handleTy.getClassSym(); + + ModuleOp mod = op->getParentOfType(); + + if (failed(resolveClassStructBody(mod, sym, *typeConverter, cache))) + return op.emitError() << "Could not resolve class struct for " << sym; + + auto structTy = cache.getStructInfo(sym)->classBody; + + DataLayout dl(mod); + // DataLayout::getTypeSize gives a byte count for LLVM types. + uint64_t byteSize = dl.getTypeSize(structTy); + auto i64Ty = IntegerType::get(ctx, 64); + auto cSize = LLVM::ConstantOp::create(rewriter, loc, i64Ty, + rewriter.getI64IntegerAttr(byteSize)); + + // Get or declare malloc and call it. + auto mallocFn = getOrCreateMalloc(mod, rewriter); + auto ptrTy = LLVM::LLVMPointerType::get(ctx); // opaque pointer result + auto call = + LLVM::CallOp::create(rewriter, loc, TypeRange{ptrTy}, + SymbolRefAttr::get(mallocFn), ValueRange{cSize}); + + // Replace the new op with the malloc pointer (no cast needed with opaque + // ptrs). + rewriter.replaceOp(op, call.getResult()); + return success(); + } + +private: + ClassTypeCache &cache; // shared, owned by the pass +}; + struct ClassDeclOpConversion : public OpConversionPattern { ClassDeclOpConversion(TypeConverter &tc, MLIRContext *ctx, ClassTypeCache &cache) @@ -686,9 +761,9 @@ struct ClassDeclOpConversion : public OpConversionPattern { LogicalResult matchAndRewrite(ClassDeclOp op, OpAdaptor, ConversionPatternRewriter &rewriter) const override { + if (failed(resolveClassStructBody(op, *typeConverter, cache))) return failure(); - // The declaration itself is a no-op rewriter.eraseOp(op); return success(); @@ -2077,7 +2152,8 @@ static void populateOpConversion(ConversionPatternSet &patterns, patterns.add(typeConverter, patterns.getContext(), classCache); - + patterns.add(typeConverter, patterns.getContext(), + classCache); // clang-format off patterns.add< // Patterns of declaration operations. diff --git a/test/Conversion/MooreToCore/classes.mlir b/test/Conversion/MooreToCore/classes.mlir index f6a6d9991cf9..f3f9fe7c5929 100644 --- a/test/Conversion/MooreToCore/classes.mlir +++ b/test/Conversion/MooreToCore/classes.mlir @@ -17,3 +17,66 @@ moore.class.classdecl @PropertyCombo { func.func @ClassType(%arg0: !moore.class<@PropertyCombo>) { return } + +/// Check that new lowers to malloc + +// malloc should be declared in the LLVM dialect. +// CHECK-LABEL: func.func private @test_new2 +// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(12 : i64) : i64 +// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr +// CHECK: return + +// CHECK-NOT: moore.class.new +// CHECK-NOT: moore.class.classdecl + +// Allocate a new instance; should lower to llvm.call @malloc(i64). +func.func private @test_new2() { + %h = moore.class.new : <@C> + return +} +// Minimal class so the identified struct has a concrete body. +moore.class.classdecl @C { + moore.class.propertydecl @a : !moore.i32 + moore.class.propertydecl @b : !moore.l32 + moore.class.propertydecl @c : !moore.l32 +} + +/// Check that new lowers to malloc with inheritance without shadowing + +// CHECK-LABEL: func.func private @test_new3 +// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(28 : i64) : i64 +// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr +// CHECK: return + +// CHECK-NOT: moore.class.new +// CHECK-NOT: moore.class.classdecl + +func.func private @test_new3() { + %h = moore.class.new : <@D> + return +} +moore.class.classdecl @D extends @C { + moore.class.propertydecl @d : !moore.l32 + moore.class.propertydecl @e : !moore.l64 + moore.class.propertydecl @f : !moore.i16 +} + +/// Check that new lowers to malloc with inheritance & shadowing + +// CHECK-LABEL: func.func private @test_new4 +// CHECK: [[SIZE:%.*]] = llvm.mlir.constant(24 : i64) : i64 +// CHECK: [[PTR:%.*]] = llvm.call @malloc([[SIZE]]) : (i64) -> !llvm.ptr +// CHECK: return + +// CHECK-NOT: moore.class.new +// CHECK-NOT: moore.class.classdecl + +func.func private @test_new4() { + %h = moore.class.new : <@E> + return +} +moore.class.classdecl @E extends @C { + moore.class.propertydecl @a : !moore.i32 + moore.class.propertydecl @b : !moore.l32 + moore.class.propertydecl @c : !moore.l32 +}