Skip to content

[CIR] Implement CGM.getIntrinsic for LLVMCallIntrinsicOp #1710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
224 changes: 224 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRIntrinsics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
//===- CIRIntrinsics.h - CIR Intrinsic Function Handling ----------*- C++
//-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines a set of enums which allow processing of intrinsic
// functions. Values of these enum types are returned by
// Function::getIntrinsicID.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_CIR_DIALECT_INTRINSICS_H
#define LLVM_CLANG_CIR_DIALECT_INTRINSICS_H

#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/SymbolTable.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/TypeSize.h"
#include <optional>
#include <string>
// #include "mlir/IR/Types.h"
#include "mlir/Interfaces/DataLayoutInterfaces.h"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
#include "clang/CIR/Interfaces/ASTAttrInterfaces.h"
#include "clang/CIR/Interfaces/CIRFPTypeInterface.h"

namespace mlir {
class Type;
class ModuleOp;
class MLIRContext;
} // namespace mlir

namespace llvm {
class StringRef;

namespace Intrinsic {
struct IITDescriptor; // No need to duplicate the definition here
} // namespace Intrinsic

} // namespace llvm
namespace cir {
class LLVMIntrinsicCallOp;
class FuncOp;
// FIXME: Unsure if we need a proper function type

namespace CIRIntrinsic {

// Abstraction for the arguments of the noalias intrinsics
static const int NoAliasScopeDeclScopeArg = 0;

// Intrinsic ID type. This is an opaque typedef to facilitate splitting up
// the enum into target-specific enums.
typedef unsigned ID;

enum IndependentIntrinsics : unsigned {
not_intrinsic = 0, // Must be zero

// Get the intrinsic enums generated from Intrinsics.td
#define GET_INTRINSIC_ENUM_VALUES
#include "llvm/IR/IntrinsicEnums.inc"
#undef GET_INTRINSIC_ENUM_VALUES
};

// Simple descriptor struct that holds essential intrinsic information
// In order to build CIRIntrinsicCallOp
struct IntrinsicDescriptor {
mlir::StringAttr name; // Mangled name attribute
mlir::Type resultType; // Return type for the intrinsic
ID id; // Original intrinsic ID (optional)

// Basic constructor
IntrinsicDescriptor(mlir::StringAttr name, mlir::Type resultType,
ID id = not_intrinsic)
: name(name), resultType(resultType), id(id) {}

// Default constructor for empty/invalid descriptors
IntrinsicDescriptor()
: name(nullptr), resultType(nullptr), id(not_intrinsic) {}

// Check if descriptor is valid
bool isValid() const { return name && resultType; }
};

/// Return the LLVM name for an intrinsic, such as "llvm.ppc.altivec.lvx".
/// Note, this version is for intrinsics with no overloads. Use the other
/// version of getName if overloads are required.
llvm::StringRef getName(ID id);

/// Return the LLVM name for an intrinsic, without encoded types for
/// overloading, such as "llvm.ssa.copy".
llvm::StringRef getBaseName(ID id);

/// Return the LLVM name for an intrinsic, such as "llvm.ppc.altivec.lvx" or
/// "llvm.ssa.copy.p0s_s.1". Note, this version of getName supports overloads.
/// This is less efficient than the StringRef version of this function. If no
/// overloads are required, it is safe to use this version, but better to use
/// the StringRef version. If one of the types is based on an unnamed type, a
/// function type will be computed. Providing FT will avoid this computation.
std::string getName(ID Id, llvm::ArrayRef<mlir::Type> Tys, mlir::ModuleOp M,
mlir::Type FT = nullptr);

/// Return the LLVM name for an intrinsic. This is a special version only to
/// be used by LLVMIntrinsicCopyOverloadedName. It only supports overloads
/// based on named types.
std::string getNameNoUnnamedTypes(ID Id, llvm::ArrayRef<mlir::Type> Tys);

/// Return the function type for an intrinsic.
// mlir::Type getType(mlir::MLIRContext &Context, ID id,
// llvm::ArrayRef<mlir::Type> Tys = {});

// Get both return type and parameter types in one call
mlir::Type getType(mlir::MLIRContext &Context, ID id,
llvm::ArrayRef<mlir::Type> Tys);

/// Returns true if the intrinsic can be overloaded.
bool isOverloaded(ID id); // NYI

ID lookupIntrinsicID(llvm::StringRef Name);

// FIXME: Uses table from LLVM, but we don't have it yet.
/// Return the attributes for an intrinsic.
// AttributeList getAttributes(mlir::MLIRContext &C, ID id);
// this is also defined as:
// /// This defines the "Intrinsic::getAttributes(ID id)" method.
// #define GET_INTRINSIC_ATTRIBUTES
// #include "llvm/IR/IntrinsicImpl.inc"
// #undef GET_INTRINSIC_ATTRIBUTES

/// Look up the Function declaration of the intrinsic \p id in the Module
/// \p M. If it does not exist, add a declaration and return it. Otherwise,
/// return the existing declaration.
///
/// The \p Tys parameter is for intrinsics with overloaded types (e.g., those
/// using iAny, fAny, vAny, or pAny). For a declaration of an overloaded
/// intrinsic, Tys must provide exactly one type for each overloaded type in
/// the intrinsic.
IntrinsicDescriptor getOrInsertDeclaration(mlir::ModuleOp M, ID id,
llvm::ArrayRef<mlir::Type> Tys = {});

/// Look up the Function declaration of the intrinsic \p id in the Module
/// \p M and return it if it exists. Otherwise, return nullptr. This version
/// supports non-overloaded intrinsics.
IntrinsicDescriptor getDeclarationIfExists(const mlir::ModuleOp *M, ID id);

/// This version supports overloaded intrinsics.
IntrinsicDescriptor getDeclarationIfExists(mlir::ModuleOp M, ID id,
llvm::ArrayRef<mlir::Type> Tys,
mlir::Type FT = nullptr);

/// Map a Clang builtin name to an intrinsic ID.
ID getIntrinsicForClangBuiltin(llvm::StringRef TargetPrefix,
llvm::StringRef BuiltinName);

/// Map a MS builtin name to an intrinsic ID.
ID getIntrinsicForMSBuiltin(llvm::StringRef TargetPrefix,
llvm::StringRef BuiltinName);

// FIXME: Uses table from LLVM, but we don't have it yet.
// /// Returns true if the intrinsic ID is for one of the "Constrained
// /// Floating-Point Intrinsics".
// bool isConstrainedFPIntrinsic(ID QID);

// /// Returns true if the intrinsic ID is for one of the "Constrained
// /// Floating-Point Intrinsics" that take rounding mode metadata.
// bool hasConstrainedFPRoundingModeOperand(ID QID);

/// Return the IIT table descriptor for the specified intrinsic into an array
/// of IITDescriptors.
void getIntrinsicInfoTableEntries(
ID id, llvm::SmallVectorImpl<llvm::Intrinsic::IITDescriptor> &T);

enum MatchIntrinsicTypesResult {
MatchIntrinsicTypes_Match = 0,
MatchIntrinsicTypes_NoMatchRet = 1,
MatchIntrinsicTypes_NoMatchArg = 2,
};

/// Match the specified function type with the type constraints specified by
/// the .td file. If the given type is an overloaded type it is pushed to the
/// ArgTys vector.
///
/// Returns false if the given type matches with the constraints, true
/// otherwise.
MatchIntrinsicTypesResult
matchIntrinsicSignature(FuncOp FTy,
llvm::ArrayRef<llvm::Intrinsic::IITDescriptor> &Infos,
llvm::SmallVectorImpl<mlir::Type> &ArgTys);

/// Verify if the intrinsic has variable arguments. This method is intended to
/// be called after all the fixed arguments have been matched first.
///
/// This method returns true on error.
bool matchIntrinsicVarArg(
bool isVarArg, llvm::ArrayRef<llvm::Intrinsic::IITDescriptor> &Infos);

/// Gets the type arguments of an intrinsic call by matching type contraints
/// specified by the .td file. The overloaded types are pushed into the
/// AgTys vector.
///
/// Returns false if the given ID and function type combination is not a
/// valid intrinsic call.
bool getIntrinsicSignature(ID, mlir::Type FT,
llvm::SmallVectorImpl<mlir::Type> &ArgTys);

/// Same as previous, but accepts a Function instead of ID and FunctionType.
bool getIntrinsicSignature(FuncOp F, llvm::SmallVectorImpl<mlir::Type> &ArgTys);

// Checks if the intrinsic name matches with its signature and if not
// returns the declaration with the same signature and remangled name.
// An existing GlobalValue with the wanted name but with a wrong prototype
// or of the wrong kind will be renamed by adding ".renamed" to the name.
std::optional<LLVMIntrinsicCallOp> remangleIntrinsicFunction(FuncOp F);

} // namespace CIRIntrinsic
} // namespace cir

#endif // LLVM_CLANG_CIR_DIALECT_INTRINSICS_H
12 changes: 12 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Address.h"
#include "CIRGenRecordLayout.h"
#include "CIRGenTypeCache.h"
#include "clang/CIR/Dialect/IR/CIRIntrinsics.h"
#include "clang/CIR/MissingFeatures.h"

#include "clang/AST/Decl.h"
Expand Down Expand Up @@ -749,6 +750,17 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return create<cir::VTTAddrPointOp>(loc, retTy, sym, mlir::Value{}, offset);
}

mlir::Value
createIntrinsicCall(mlir::Location loc,
cir::CIRIntrinsic::IntrinsicDescriptor descriptor,
llvm::ArrayRef<mlir::Value> args = {}) {
assert(descriptor.isValid() && "invalid intrinsic descriptor");

return create<cir::LLVMIntrinsicCallOp>(loc, descriptor.name,
descriptor.resultType, args)
.getResult();
}

// FIXME(cir): CIRGenBuilder class should have an attribute with a reference
// to the module so that we don't have search for it or pass it around.
// FIXME(cir): Track a list of globals, or at least the last one inserted, so
Expand Down
54 changes: 19 additions & 35 deletions clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "clang/Basic/TargetBuiltins.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsX86.h"
#include "llvm/Support/ErrorHandling.h"

using namespace clang;
Expand Down Expand Up @@ -98,51 +100,33 @@ mlir::Value CIRGenFunction::emitX86BuiltinExpr(unsigned BuiltinID,
llvm_unreachable("_mm_prefetch NYI");
}
case X86::BI_mm_clflush: {
mlir::Type voidTy = cir::VoidType::get(&getMLIRContext());
return builder
.create<cir::LLVMIntrinsicCallOp>(
getLoc(E->getExprLoc()), builder.getStringAttr("x86.sse2.clflush"),
voidTy, Ops[0])
.getResult();
return builder.createIntrinsicCall(
getLoc(E->getExprLoc()),
CGM.getIntrinsic(llvm::Intrinsic::x86_sse2_clflush), Ops[0]);
}
case X86::BI_mm_lfence: {
mlir::Type voidTy = cir::VoidType::get(&getMLIRContext());
return builder
.create<cir::LLVMIntrinsicCallOp>(
getLoc(E->getExprLoc()), builder.getStringAttr("x86.sse2.lfence"),
voidTy)
.getResult();
return builder.createIntrinsicCall(
getLoc(E->getExprLoc()),
CGM.getIntrinsic(llvm::Intrinsic::x86_sse2_lfence));
}
case X86::BI_mm_pause: {
mlir::Type voidTy = cir::VoidType::get(&getMLIRContext());
return builder
.create<cir::LLVMIntrinsicCallOp>(
getLoc(E->getExprLoc()), builder.getStringAttr("x86.sse2.pause"),
voidTy)
.getResult();
return builder.createIntrinsicCall(
getLoc(E->getExprLoc()),
CGM.getIntrinsic(llvm::Intrinsic::x86_sse2_pause));
}
case X86::BI_mm_mfence: {
mlir::Type voidTy = cir::VoidType::get(&getMLIRContext());
return builder
.create<cir::LLVMIntrinsicCallOp>(
getLoc(E->getExprLoc()), builder.getStringAttr("x86.sse2.mfence"),
voidTy)
.getResult();
return builder.createIntrinsicCall(
getLoc(E->getExprLoc()),
CGM.getIntrinsic(llvm::Intrinsic::x86_sse2_mfence));
}
case X86::BI_mm_sfence: {
mlir::Type voidTy = cir::VoidType::get(&getMLIRContext());
return builder
.create<cir::LLVMIntrinsicCallOp>(
getLoc(E->getExprLoc()), builder.getStringAttr("x86.sse.sfence"),
voidTy)
.getResult();
return builder.createIntrinsicCall(
getLoc(E->getExprLoc()),
CGM.getIntrinsic(llvm::Intrinsic::x86_sse_sfence));
}
case X86::BI__rdtsc: {
mlir::Type intTy = cir::IntType::get(&getMLIRContext(), 64, false);
return builder
.create<cir::LLVMIntrinsicCallOp>(
getLoc(E->getExprLoc()), builder.getStringAttr("x86.rdtsc"), intTy)
.getResult();
return builder.createIntrinsicCall(
getLoc(E->getExprLoc()), CGM.getIntrinsic(llvm::Intrinsic::x86_rdtsc));
}
case X86::BI__builtin_ia32_rdtscp: {
llvm_unreachable("__rdtscp NYI");
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "mlir/IR/Verifier.h"
#include "clang/AST/Expr.h"
#include "clang/Basic/Cuda.h"
#include "clang/CIR/Dialect/IR/CIRIntrinsics.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"

Expand Down Expand Up @@ -2477,6 +2478,12 @@ void CIRGenModule::emitAliasForGlobal(StringRef mangledName,
setCommonAttributes(aliasGD, alias);
}

cir::CIRIntrinsic::IntrinsicDescriptor
CIRGenModule::getIntrinsic(unsigned IID, ArrayRef<mlir::Type> Tys) {
return cir::CIRIntrinsic::getOrInsertDeclaration(
getModule(), (cir::CIRIntrinsic::ID)IID, Tys);
}

mlir::Type CIRGenModule::convertType(QualType type) {
return genTypes.convertType(type);
}
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRDataLayout.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIRIntrinsics.h"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/Interfaces/CIROpInterfaces.h"
Expand Down Expand Up @@ -655,7 +656,8 @@ class CIRGenModule : public CIRGenTypeCache {
void emitAliasForGlobal(llvm::StringRef mangledName, mlir::Operation *op,
GlobalDecl aliasGD, cir::FuncOp aliasee,
cir::GlobalLinkageKind linkage);

cir::CIRIntrinsic::IntrinsicDescriptor
getIntrinsic(unsigned IID, ArrayRef<mlir::Type> Tys = {});
mlir::Type convertType(clang::QualType type);

/// Set the visibility for the given global.
Expand Down
Loading
Loading