68 changes: 34 additions & 34 deletions clang/test/Headers/wasm.c

Large diffs are not rendered by default.

159 changes: 159 additions & 0 deletions llvm/include/llvm/Transforms/Utils/InferCallsiteAttrs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//===- InferCallsiteAttrs.h - Propagate attributes to callsites -----------===//
//
// 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 declares the InferCallsiteAttrs class.
// This class is used to propagate attributes present in the caller function of
// the callsite to the arguments/return/callsite itself.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_TRANSFORMS_UTILS_INFERCALLSITEATTRS_H
#define LLVM_TRANSFORMS_UTILS_INFERCALLSITEATTRS_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"

namespace llvm {
class InferCallsiteAttrs {
enum : uint8_t { kMaybe = 0, kYes = 1, kNo = 2 };

// Limit maximum amount of instructions we will check. Everything is O(1) so
// relatively high value is okay.
static constexpr unsigned kMaxChecks = UINT_MAX;

struct FunctionInfos {
uint8_t LandingOrEHPad : 2;
};

struct BasicBlockInfos {
uint8_t Alloca : 2;
uint8_t UnkMalloc : 2;

bool isSet() const { return Alloca != kMaybe && UnkMalloc != kMaybe; }
};

struct CallsiteInfos {
uint16_t StoresBetweenReturn : 2;
uint16_t LoadsBetweenReturn : 2;
uint16_t NonDirectTransferBetweenReturn : 2;
uint16_t CallerReturnBasedOnCallsite : 2;
uint16_t IsLastInsBeforeReturn : 2;
uint16_t PrecedingAlloca : 2;
uint16_t PrecedingLocalMalloc : 2;
};

DenseMap<const BasicBlock *, BasicBlockInfos> BBInfos;
DenseMap<const Function *, FunctionInfos> FunctionInfos;
const Function *Caller;
const CallBase *CxtCB;

CallsiteInfos CurCBInfo;
struct FunctionInfos CurFnInfo;
bool PreserveCache;

// Wrapper for attribute checks that check both the context callsite and
// actual calling function.
bool checkCallerHasFnAttr(Attribute::AttrKind Attr) const {
return (CxtCB && CxtCB->hasFnAttr(Attr)) || Caller->hasFnAttribute(Attr);
};
bool checkCallerHasParamAttr(unsigned ArgIdx,
Attribute::AttrKind Attr) const {
return (CxtCB && CxtCB->paramHasAttr(ArgIdx, Attr)) ||
Caller->getArg(ArgIdx)->hasAttribute(Attr);
};
bool checkCallerHasReturnAttr(Attribute::AttrKind Attr) const {
return (CxtCB && CxtCB->hasRetAttr(Attr)) || Caller->hasRetAttribute(Attr);
};

bool checkCallerDoesNotThrow() const {
return (CxtCB && CxtCB->doesNotThrow()) || Caller->doesNotThrow();
}
bool checkCallerDoesNotAccessMemory() const {
return (CxtCB && CxtCB->doesNotAccessMemory()) ||
Caller->doesNotAccessMemory();
};
bool checkCallerOnlyReadsMemory() const {
return (CxtCB && CxtCB->onlyReadsMemory()) || Caller->onlyReadsMemory();
};
bool checkCallerOnlyWritesMemory() const {
return (CxtCB && CxtCB->onlyWritesMemory()) || Caller->onlyWritesMemory();
};
bool checkCallerOnlyAccessesArgMemory() const {
return (CxtCB && CxtCB->onlyAccessesArgMemory()) ||
Caller->onlyAccessesArgMemory();
};
bool checkCallerOnlyAccessesInaccessibleMemory() const {
return (CxtCB && CxtCB->onlyAccessesInaccessibleMemory()) ||
Caller->onlyAccessesInaccessibleMemory();
};
bool checkCallerOnlyAccessesInaccessibleMemOrArgMem() const {
return (CxtCB && CxtCB->onlyAccessesInaccessibleMemOrArgMem()) ||
Caller->onlyAccessesInaccessibleMemOrArgMem();
};

// Check all instructions between callbase and end of basicblock (if that
// basic block ends in a return). This will cache the analysis information.
// Will break early if condition is met based on arguments.
bool checkBetweenCallsiteAndReturn(const CallBase *CB, bool BailOnStore,
bool BailOnLoad,
bool BailOnNonDirectTransfer,
bool BailOnNotReturned);

// Check all instruction instructions preceding basic blocked (any instruction
// that may reach the callsite CB). If conditions are met, can set early
// return using BailOn* arguments.
bool checkPrecedingBBIns(const CallBase *CB, bool BailOnAlloca,
bool BailOnLocalMalloc);

// Check all basic blocks for conditions. At the moment only condition is if
// landing/EH pad so will store result and break immediately if one is found.
// In the future may be extended to check other conditions.
bool checkAllBBs(bool BailOnPad);

// Try to propagate nocapture attribute from caller argument to callsite
// arguments.
bool tryPropagateNoCapture(CallBase *CB);

// Try trivial propagations (one where if true for the caller, are
// automatically true for the callsite without further analysis).
bool tryTrivialPropagations(CallBase *CB);

// Try propagations of return attributes (nonnull, noundef, etc...)
bool tryReturnPropagations(CallBase *CB);

// Try propagations of memory access attribute (readnone, readonly, etc...).
bool tryMemoryPropagations(CallBase *CB);

// Add attributes to callsite, assumes Caller and CxtCB are setup already
bool inferCallsiteAttributesImpl(CallBase *CB);

public:
// Set PreserveCacheBetweenFunctions to keep cached information on
// functions/basicblocks between calls processFunction.
InferCallsiteAttrs(bool PreserveCacheBetweenFunctions = false)
: PreserveCache(PreserveCacheBetweenFunctions) {}

// Call if either 1) BB instructions have changed which may invalidate some of
// the prior analysis or 2) all previous work no longer applies (in which case
// clearing the cache improves performance).
void resetCache();

// Add attributes to callsites based on the function is called in (or by
// setting CxtCallsite the exact callsite of the callsite).
bool inferCallsiteAttributes(CallBase *CB,
const CallBase *CxtCallsite = nullptr);

// Process all callsites in Function ParentFunc. This is more efficient that
// calling inferCallsiteAttributes in a loop as it 1) avoids some unnecessary
// cache lookups and 2) does some analysis while searching for callsites.
bool processFunction(Function *ParentFunc,
const CallBase *ParentCallsite = nullptr);
};
} // namespace llvm
#endif
14 changes: 12 additions & 2 deletions llvm/lib/Transforms/IPO/FunctionAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Utils/InferCallsiteAttrs.h"
#include "llvm/Transforms/Utils/Local.h"
#include <cassert>
#include <iterator>
Expand All @@ -68,6 +69,7 @@ using namespace llvm;

#define DEBUG_TYPE "function-attrs"


STATISTIC(NumMemoryAttr, "Number of functions with improved memory attribute");
STATISTIC(NumNoCapture, "Number of arguments marked nocapture");
STATISTIC(NumReturned, "Number of arguments marked returned");
Expand Down Expand Up @@ -638,7 +640,7 @@ determinePointerAccessAttrs(Argument *A,
if (Visited.insert(&UU).second)
Worklist.push_back(&UU);
}

if (CB.doesNotAccessMemory())
continue;

Expand Down Expand Up @@ -1745,7 +1747,7 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter) {
addNoRecurseAttrs(Nodes.SCCNodes, Changed);
}

// Finally, infer the maximal set of attributes from the ones we've inferred
// Infer the maximal set of attributes from the ones we've inferred
// above. This is handling the cases where one attribute on a signature
// implies another, but for implementation reasons the inference rule for
// the later is missing (or simply less sophisticated).
Expand All @@ -1754,6 +1756,14 @@ deriveAttrsInPostOrder(ArrayRef<Function *> Functions, AARGetterT &&AARGetter) {
if (inferAttributesFromOthers(*F))
Changed.insert(F);

// Finally, propagate the functions attributes to all the callsites inside of
// it.
InferCallsiteAttrs ICA;
for (Function *F : Nodes.SCCNodes)
if (F)
if (ICA.processFunction(F))
Changed.insert(F);

return Changed;
}

Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ add_llvm_component_library(LLVMTransformUtils
GlobalStatus.cpp
GuardUtils.cpp
HelloWorld.cpp
InferCallsiteAttrs.cpp
InlineFunction.cpp
InjectTLIMappings.cpp
InstructionNamer.cpp
Expand Down
725 changes: 725 additions & 0 deletions llvm/lib/Transforms/Utils/InferCallsiteAttrs.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion llvm/test/Other/cgscc-devirt-iteration.ll
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ entry:
; This indirect call is the first to be resolved, allowing us to deduce
; readonly but not (yet) readnone.
call void %f1(ptr %ignore)
; CHECK: call void @readnone_with_arg(ptr %ignore)
; CHECK: call void @readnone_with_arg(ptr

; Bogus call to test2_b to make this a cycle.
call void @test2_b()
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/Transforms/FunctionAttrs/nonnull.ll
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ define void @parent1(ptr %a, ptr %b, ptr %c) {
define void @parent2(ptr %a, ptr %b, ptr %c) {
; FNATTR-LABEL: @parent2(ptr nonnull %a, ptr nonnull %b, ptr nonnull %c)
; FNATTR-NEXT: call void @use3nonnull(ptr %b, ptr %c, ptr %a)
; FNATTR-NEXT: call void @use3(ptr %c, ptr %a, ptr %b)
; FNATTR-NEXT: call void @use3(ptr nonnull %c, ptr nonnull %a, ptr nonnull %b)


; FNATTR-NEXT: ret void
Expand All @@ -371,7 +371,7 @@ define void @parent2(ptr %a, ptr %b, ptr %c) {
define void @parent3(ptr %a, ptr %b, ptr %c) {
; FNATTR-LABEL: @parent3(ptr nonnull %a, ptr %b, ptr %c)
; FNATTR-NEXT: call void @use1nonnull(ptr %a)
; FNATTR-NEXT: call void @use3(ptr %c, ptr %b, ptr %a)
; FNATTR-NEXT: call void @use3(ptr %c, ptr %b, ptr nonnull %a)


; FNATTR-NEXT: ret void
Expand Down Expand Up @@ -437,7 +437,7 @@ define i8 @parent6(ptr %a, ptr %b) {

define i8 @parent7(ptr %a) {
; FNATTR-LABEL: @parent7(ptr nonnull %a)
; FNATTR-NEXT: [[RET:%.*]] = call i8 @use1safecall(ptr %a)
; FNATTR-NEXT: [[RET:%.*]] = call i8 @use1safecall(ptr nonnull %a)
; FNATTR-NEXT: call void @use1nonnull(ptr %a)


Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Transforms/FunctionAttrs/readattrs.ll
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ define void @test4_2(ptr %p) {
; CHECK: Function Attrs: nofree memory(read)
; CHECK-LABEL: define {{[^@]+}}@test4_2
; CHECK-SAME: (ptr nocapture readonly [[P:%.*]]) #[[ATTR3:[0-9]+]] {
; CHECK-NEXT: call void @test4_1(ptr [[P]])
; CHECK-NEXT: call void @test4_1(ptr readonly [[P]])
; CHECK-NEXT: ret void
;
call void @test4_1(ptr %p)
Expand Down Expand Up @@ -115,7 +115,7 @@ define void @test8_2(ptr %p) {
; CHECK-LABEL: define {{[^@]+}}@test8_2
; CHECK-SAME: (ptr writeonly [[P:%.*]]) #[[ATTR4]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CALL:%.*]] = call ptr @test8_1(ptr [[P]])
; CHECK-NEXT: [[CALL:%.*]] = call ptr @test8_1(ptr writeonly [[P]])
; CHECK-NEXT: store i32 10, ptr [[CALL]], align 4
; CHECK-NEXT: ret void
;
Expand Down
24 changes: 12 additions & 12 deletions llvm/test/Transforms/FunctionAttrs/willreturn-callsites.ll
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ declare void @decl_unknown()
define void @test_fn_mustprogress(ptr %ptr) mustprogress {
; CHECK: Function Attrs: mustprogress
; CHECK-LABEL: @test_fn_mustprogress(
; CHECK-NOT: call void @decl_readonly() #
; CHECK-NOT: call void @decl_readnone() #
; CHECK-NOT: call void @decl_unknown() #
; CHECK-NOT: call void @decl_argmemonly(ptr [[PTR:%.*]]) #
; CHECK: call void @decl_readonly() #
; CHECK: call void @decl_readnone() #
; CHECK: call void @decl_unknown() #
; CHECK: call void @decl_argmemonly(ptr [[PTR:%.*]]) #
; CHECK: ret void
;
call void @decl_readonly()
Expand All @@ -24,10 +24,10 @@ define void @test_fn_mustprogress(ptr %ptr) mustprogress {
define void @test_fn_willreturn(ptr %ptr) willreturn {
; CHECK: Function Attrs: mustprogress willreturn
; CHECK-LABEL: @test_fn_willreturn(
; CHECK-NOT: call void @decl_readonly() #
; CHECK-NOT: call void @decl_readnone() #
; CHECK-NOT: call void @decl_unknown() #
; CHECK-NOT: call void @decl_argmemonly(ptr [[PTR:%.*]]) #
; CHECK: call void @decl_readonly() #
; CHECK: call void @decl_readnone() #
; CHECK: call void @decl_unknown() #
; CHECK: call void @decl_argmemonly(ptr [[PTR:%.*]]) #
; CHECK: ret void
;
call void @decl_readonly()
Expand All @@ -40,8 +40,8 @@ define void @test_fn_willreturn(ptr %ptr) willreturn {
define void @test_fn_mustprogress_readonly_calls(ptr %ptr) mustprogress {
; CHECK: Function Attrs: mustprogress nofree willreturn memory(read)
; CHECK-LABEL: @test_fn_mustprogress_readonly_calls(
; CHECK-NOT: call void @decl_readonly() #
; CHECK-NOT: call void @decl_readnone() #
; CHECK: call void @decl_readonly() #
; CHECK: call void @decl_readnone() #
; CHECK: ret void
;
call void @decl_readonly()
Expand All @@ -52,8 +52,8 @@ define void @test_fn_mustprogress_readonly_calls(ptr %ptr) mustprogress {
define void @test_fn_mustprogress_readonly_calls_but_stores(ptr %ptr) mustprogress {
; CHECK: Function Attrs: mustprogress nofree
; CHECK-LABEL: @test_fn_mustprogress_readonly_calls_but_stores(
; CHECK-NOT: call void @decl_readonly() #
; CHECK-NOT: call void @decl_readnone() #
; CHECK: call void @decl_readonly() #
; CHECK: call void @decl_readnone() #
; CHECK: store i32 0, ptr [[PTR:%.*]], align 4
; CHECK-NEXT: ret void
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ define i32 @maxB(i32 %x, i32 %y) !dbg !34 {

; OPTIMIZATION_LEVEL_2: define i32 @maxB(i32 %x, i32 %y)
; OPTIMIZATION_LEVEL_2-NEXT: entry:
; OPTIMIZATION_LEVEL_2-NEXT: call void @llvm.dbg.value(metadata i32 %x, metadata !{{[0-9]+}}, metadata !DIExpression()), !dbg !{{[0-9]+}}
; OPTIMIZATION_LEVEL_2-NEXT: call void @llvm.dbg.value(metadata i32 %y, metadata !{{[0-9]+}}, metadata !DIExpression()), !dbg !{{[0-9]+}}
; OPTIMIZATION_LEVEL_2-NEXT: call void @llvm.dbg.value(metadata i32 %x, metadata !{{[0-9]+}}, metadata !DIExpression()) #{{[0-9]+}}, !dbg !{{[0-9]+}}
; OPTIMIZATION_LEVEL_2-NEXT: call void @llvm.dbg.value(metadata i32 %y, metadata !{{[0-9]+}}, metadata !DIExpression()) #{{[0-9]+}}, !dbg !{{[0-9]+}}
; OPTIMIZATION_LEVEL_2-NEXT: %0 = tail call i32 @maxA(i32 %x, i32 %y) #{{[0-9]+}}, !dbg !{{[0-9]+}}
; OPTIMIZATION_LEVEL_2-NEXT: ret i32 %0, !dbg !{{[0-9]+}}
; OPTIMIZATION_LEVEL_2-NEXT: }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ define i32 @cttz(i32 %n, ptr %p1) {
; ALL-LABEL: @cttz(
; ALL-NEXT: entry:
; ALL-NEXT: [[TMP0:%.*]] = shl i32 [[N:%.*]], 1
; ALL-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.cttz.i32(i32 [[TMP0]], i1 false), !range [[RNG0:![0-9]+]]
; ALL-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.cttz.i32(i32 [[TMP0]], i1 false) #[[ATTR2:[0-9]+]], !range [[RNG0:![0-9]+]]
; ALL-NEXT: [[TMP2:%.*]] = sub nuw nsw i32 32, [[TMP1]]
; ALL-NEXT: [[TMP3:%.*]] = sub nuw nsw i32 75, [[TMP1]]
; ALL-NEXT: store i32 [[TMP3]], ptr [[P1:%.*]], align 4
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/PhaseOrdering/memset-tail.ll
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ define void @PR47852(ptr noundef %d, i32 noundef %c) {
; CHECK-NEXT: br i1 [[CMP_NOT1]], label [[WHILE_END:%.*]], label [[WHILE_BODY_PREHEADER:%.*]]
; CHECK: while.body.preheader:
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[C]] to i64
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[D:%.*]], i8 0, i64 [[TMP0]], i1 false)
; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef align 1 [[D:%.*]], i8 0, i64 [[TMP0]], i1 false)
; CHECK-NEXT: br label [[WHILE_END]]
; CHECK: while.end:
; CHECK-NEXT: ret void
Expand Down