Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add @llvm.clear_cache builtin
Implementing the LLVM part of the call to __builtin___clear_cache
which translates into an intrinsic @llvm.clear_cache and is lowered
by each target, either to a call to __clear_cache or nothing at all
incase the caches are unified.

Updating LangRef and adding some tests for the implemented architectures.
Other archs will have to implement the method in case this builtin
has to be compiled for it, since the default behaviour is to bail
unimplemented.

A Clang patch is required for the builtin to be lowered into the
llvm intrinsic. This will be done next.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@204802 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
rengolin committed Mar 26, 2014
1 parent 159e7f4 commit c4b058f
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 0 deletions.
30 changes: 30 additions & 0 deletions docs/LangRef.rst
Expand Up @@ -6939,6 +6939,36 @@ is lowered to a constant 0.
Note that runtime support may be conditional on the privilege-level code is
running at and the host platform.

'``llvm.clear_cache``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare void @llvm.clear_cache(i8*, i8*)

Overview:
"""""""""

The '``llvm.clear_cache``' intrinsic provides access to the systemcall
that flushes the cache at the range specified. Some targets need this
to specifically flush the instruction cache when executable data changes
in memory (self-modifying code). Other targets have unified intruction
and data cache, so they don't need any calls.

Semantics:
""""""""""

When directly supported, this intrinsic will either return a call to
the appropriate cache clearing system call (usually ``__clear_cache``)
when the caches are not unified (ARM, Mips) or just remove the call
altogether when they are (ex. x86_64).

Targets must implement it directly to have either behaviour, as the
default is to bail with "Not Implemented" message.

Standard C Library Intrinsics
-----------------------------

Expand Down
5 changes: 5 additions & 0 deletions include/llvm/IR/Intrinsics.td
Expand Up @@ -513,6 +513,11 @@ def int_convertus : Intrinsic<[llvm_anyint_ty],
def int_convertuu : Intrinsic<[llvm_anyint_ty],
[llvm_anyint_ty, llvm_i32_ty, llvm_i32_ty]>;

// Clear cache intrinsic, default to ignore (ie. emit nothing)
// maps to void __clear_cache() on supporting platforms
def int_clear_cache : Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty],
[], "llvm.clear_cache">;

//===----------------------------------------------------------------------===//
// Target-specific intrinsics
//===----------------------------------------------------------------------===//
Expand Down
5 changes: 5 additions & 0 deletions include/llvm/Target/TargetLowering.h
Expand Up @@ -2108,6 +2108,11 @@ class TargetLowering : public TargetLoweringBase {
return false;
}

/// Return the builtin name for the __builtin___clear_cache intrinsic
virtual const char * getClearCacheBuiltinName() const {
llvm_unreachable("Not Implemented");
}

/// Return the type that should be used to zero or sign extend a
/// zeroext/signext integer argument or return value. FIXME: Most C calling
/// convention requires the return type to be promoted, but this is not true
Expand Down
2 changes: 2 additions & 0 deletions lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Expand Up @@ -5386,6 +5386,8 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
(void)getControlRoot();
return 0;
}
case Intrinsic::clear_cache:
return TLI->getClearCacheBuiltinName();
case Intrinsic::donothing:
// ignore
return 0;
Expand Down
5 changes: 5 additions & 0 deletions lib/Target/ARM/ARMISelLowering.h
Expand Up @@ -384,6 +384,11 @@ namespace llvm {
bool shouldConvertConstantLoadToIntImm(const APInt &Imm,
Type *Ty) const override;

/// Clear cache library call
const char * getClearCacheBuiltinName() const {
return "__clear_cache";
}

protected:
std::pair<const TargetRegisterClass*, uint8_t>
findRepresentativeClass(MVT VT) const override;
Expand Down
5 changes: 5 additions & 0 deletions lib/Target/Mips/MipsISelLowering.h
Expand Up @@ -583,6 +583,11 @@ namespace llvm {
bool MemcpyStrSrc,
MachineFunction &MF) const;

/// Clear cache library call
const char * getClearCacheBuiltinName() const {
return "__clear_cache";
}

/// isFPImmLegal - Returns true if the target can instruction select the
/// specified FP immediate natively. If false, the legalizer will
/// materialize the FP immediate as a load from a constant pool.
Expand Down
5 changes: 5 additions & 0 deletions lib/Target/X86/X86ISelLowering.h
Expand Up @@ -770,6 +770,11 @@ namespace llvm {
bool shouldConvertConstantLoadToIntImm(const APInt &Imm,
Type *Ty) const override;

/// Intel processors have a unified instruction and data cache
const char * getClearCacheBuiltinName() const {
return 0; // nothing to do, move along.
}

/// createFastISel - This method returns a target specific FastISel object,
/// or null if the target does not support "fast" ISel.
FastISel *createFastISel(FunctionLoweringInfo &funcInfo,
Expand Down
26 changes: 26 additions & 0 deletions test/CodeGen/ARM/cache-intrinsic.ll
@@ -0,0 +1,26 @@
; RUN: llc %s -o - | FileCheck %s
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-n32-S64"
target triple = "armv7--linux-gnueabihf"

@buffer = global [32 x i8] c"This is a largely unused buffer\00", align 1
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
@.str1 = private unnamed_addr constant [25 x i8] c"Still, largely unused...\00", align 1

define i32 @main() {
entry:
%retval = alloca i32, align 4
store i32 0, i32* %retval
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0))
%call1 = call i8* @strcpy(i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i8* getelementptr inbounds ([25 x i8]* @.str1, i32 0, i32 0)) #3
call void @llvm.clear_cache(i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i8* getelementptr inbounds (i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i32 32)) #3
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0))
ret i32 0
}

; CHECK: __clear_cache

declare i32 @printf(i8*, ...)

declare i8* @strcpy(i8*, i8*)

declare void @llvm.clear_cache(i8*, i8*)
26 changes: 26 additions & 0 deletions test/CodeGen/Mips/cache-intrinsic.ll
@@ -0,0 +1,26 @@
; RUN: llc %s -o - | FileCheck %s
target datalayout = "E-p:32:32:32-i1:8:8-i8:8:32-i16:16:32-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-n32-S64"
target triple = "mips--linux-gnu"

@buffer = global [32 x i8] c"This is a largely unused buffer\00", align 1
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
@.str1 = private unnamed_addr constant [25 x i8] c"Still, largely unused...\00", align 1

define i32 @main() {
entry:
%retval = alloca i32, align 4
store i32 0, i32* %retval
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0))
%call1 = call i8* @strcpy(i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i8* getelementptr inbounds ([25 x i8]* @.str1, i32 0, i32 0)) #3
call void @llvm.clear_cache(i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i8* getelementptr inbounds (i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i32 32)) #3
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0))
ret i32 0
}

; CHECK: __clear_cache

declare i32 @printf(i8*, ...)

declare i8* @strcpy(i8*, i8*)

declare void @llvm.clear_cache(i8*, i8*)
26 changes: 26 additions & 0 deletions test/CodeGen/X86/cache-intrinsic.ll
@@ -0,0 +1,26 @@
; RUN: llc %s -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@buffer = global [32 x i8] c"This is a largely unused buffer\00", align 16
@.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
@.str1 = private unnamed_addr constant [25 x i8] c"Still, largely unused...\00", align 1

define i32 @main() {
entry:
%retval = alloca i32, align 4
store i32 0, i32* %retval
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0))
%call1 = call i8* @strcpy(i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i8* getelementptr inbounds ([25 x i8]* @.str1, i32 0, i32 0)) #3
call void @llvm.clear_cache(i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i8* getelementptr inbounds (i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0), i32 32)) #3
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([32 x i8]* @buffer, i32 0, i32 0))
ret i32 0
}

; CHECK-NOT: __clear_cache

declare i32 @printf(i8*, ...)

declare i8* @strcpy(i8*, i8*)

declare void @llvm.clear_cache(i8*, i8*)

0 comments on commit c4b058f

Please sign in to comment.