Skip to content
This repository has been archived by the owner on Mar 28, 2020. It is now read-only.

Commit

Permalink
Merge remote-tracking branch 'origin/apple/stable/20190619' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
swift-ci committed Oct 9, 2019
2 parents 36a5e1a + 9ebee0a commit 1cf6569
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 11 deletions.
22 changes: 22 additions & 0 deletions lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3868,6 +3868,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
Address swiftErrorTemp = Address::invalid();
Address swiftErrorArg = Address::invalid();

// When passing arguments using temporary allocas, we need to add the
// appropriate lifetime markers. This vector keeps track of all the lifetime
// markers that need to be ended right after the call.
SmallVector<CallLifetimeEnd, 2> CallLifetimeEndAfterCall;

// Translate all of the arguments as necessary to match the IR lowering.
assert(CallInfo.arg_size() == CallArgs.size() &&
"Mismatch between function signature & arguments.");
Expand Down Expand Up @@ -3984,6 +3989,18 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
Address AI = CreateMemTempWithoutCast(
I->Ty, ArgInfo.getIndirectAlign(), "byval-temp");
IRCallArgs[FirstIRArg] = AI.getPointer();

// Emit lifetime markers for the temporary alloca.
uint64_t ByvalTempElementSize =
CGM.getDataLayout().getTypeAllocSize(AI.getElementType());
llvm::Value *LifetimeSize =
EmitLifetimeStart(ByvalTempElementSize, AI.getPointer());

// Add cleanup code to emit the end lifetime marker after the call.
if (LifetimeSize) // In case we disabled lifetime markers.
CallLifetimeEndAfterCall.emplace_back(AI, LifetimeSize);

// Generate the copy.
I->copyInto(*this, AI);
} else {
// Skip the extra memcpy call.
Expand Down Expand Up @@ -4551,6 +4568,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
}
}

// Explicitly call CallLifetimeEnd::Emit just to re-use the code even though
// we can't use the full cleanup mechanism.
for (CallLifetimeEnd &LifetimeEnd : CallLifetimeEndAfterCall)
LifetimeEnd.Emit(*this, /*Flags=*/{});

return Ret;
}

Expand Down
40 changes: 29 additions & 11 deletions lib/Lex/DependencyDirectivesSourceMinimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,17 +185,6 @@ static void skipRawString(const char *&First, const char *const End) {
}
}

static void skipString(const char *&First, const char *const End) {
assert(*First == '\'' || *First == '"' || *First == '<');
const char Terminator = *First == '<' ? '>' : *First;
for (++First; First != End && *First != Terminator; ++First)
if (*First == '\\')
if (++First == End)
return;
if (First != End)
++First; // Finish off the string.
}

// Returns the length of EOL, either 0 (no end-of-line), 1 (\n) or 2 (\r\n)
static unsigned isEOL(const char *First, const char *const End) {
if (First == End)
Expand All @@ -206,6 +195,35 @@ static unsigned isEOL(const char *First, const char *const End) {
return !!isVerticalWhitespace(First[0]);
}

static void skipString(const char *&First, const char *const End) {
assert(*First == '\'' || *First == '"' || *First == '<');
const char Terminator = *First == '<' ? '>' : *First;
for (++First; First != End && *First != Terminator; ++First) {
// String and character literals don't extend past the end of the line.
if (isVerticalWhitespace(*First))
return;
if (*First != '\\')
continue;
// Skip past backslash to the next character. This ensures that the
// character right after it is skipped as well, which matters if it's
// the terminator.
if (++First == End)
return;
if (!isWhitespace(*First))
continue;
// Whitespace after the backslash might indicate a line continuation.
const char *FirstAfterBackslashPastSpace = First;
skipOverSpaces(FirstAfterBackslashPastSpace, End);
if (unsigned NLSize = isEOL(FirstAfterBackslashPastSpace, End)) {
// Advance the character pointer to the next line for the next
// iteration.
First = FirstAfterBackslashPastSpace + NLSize - 1;
}
}
if (First != End)
++First; // Finish off the string.
}

// Returns the length of the skipped newline
static unsigned skipNewline(const char *&First, const char *End) {
if (First == End)
Expand Down
89 changes: 89 additions & 0 deletions test/CodeGen/aarch64-byval-temp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// RUN: %clang_cc1 -emit-llvm -triple arm64-- -o - %s -O0 | FileCheck %s --check-prefix=CHECK-O0
// RUN: %clang_cc1 -emit-llvm -disable-llvm-optzns -triple arm64-- -o - %s -O3 | FileCheck %s --check-prefix=CHECK-O3

struct large {
void* pointers[8];
};

void pass_large(struct large);

// For arm64, we don't use byval to pass structs but instead we create
// temporary allocas.
//
// Make sure we generate the appropriate lifetime markers for the temporary
// allocas so that the optimizer can re-use stack slots if possible.
void example() {
struct large l = {0};
pass_large(l);
pass_large(l);
}
// CHECK-O0-LABEL: define void @example(
// The alloca for the struct on the stack.
// CHECK-O0: %[[l:[0-9A-Za-z-]+]] = alloca %struct.large, align 8
// The alloca for the temporary stack space that we use to pass the argument.
// CHECK-O0-NEXT: %[[byvaltemp:[0-9A-Za-z-]+]] = alloca %struct.large, align 8
// Another one to pass the argument to the second function call.
// CHECK-O0-NEXT: %[[byvaltemp1:[0-9A-Za-z-]+]] = alloca %struct.large, align 8
// First, memset `l` to 0.
// CHECK-O0-NEXT: %[[bitcastl:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[l]] to i8*
// CHECK-O0-NEXT: call void @llvm.memset.p0i8.i64(i8* align 8 %[[bitcastl]], i8 0, i64 64, i1 false)
// Then, memcpy `l` to the temporary stack space.
// CHECK-O0-NEXT: %[[src:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[byvaltemp]] to i8*
// CHECK-O0-NEXT: %[[dst:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[l]] to i8*
// CHECK-O0-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[src]], i8* align 8 %[[dst]], i64 64, i1 false)
// Finally, call using a pointer to the temporary stack space.
// CHECK-O0-NEXT: call void @pass_large(%struct.large* %[[byvaltemp]])
// Now, do the same for the second call, using the second temporary alloca.
// CHECK-O0-NEXT: %[[src:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[byvaltemp1]] to i8*
// CHECK-O0-NEXT: %[[dst:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[l]] to i8*
// CHECK-O0-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[src]], i8* align 8 %[[dst]], i64 64, i1 false)
// CHECK-O0-NEXT: call void @pass_large(%struct.large* %[[byvaltemp1]])
// CHECK-O0-NEXT: ret void
//
// At O3, we should have lifetime markers to help the optimizer re-use the temporary allocas.
//
// CHECK-O3-LABEL: define void @example(
// The alloca for the struct on the stack.
// CHECK-O3: %[[l:[0-9A-Za-z-]+]] = alloca %struct.large, align 8
// The alloca for the temporary stack space that we use to pass the argument.
// CHECK-O3-NEXT: %[[byvaltemp:[0-9A-Za-z-]+]] = alloca %struct.large, align 8
// Another one to pass the argument to the second function call.
// CHECK-O3-NEXT: %[[byvaltemp1:[0-9A-Za-z-]+]] = alloca %struct.large, align 8
//
// Mark the start of the lifetime for `l`
// CHECK-O3-NEXT: %[[bitcastl:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[l]] to i8*
// CHECK-O3-NEXT: call void @llvm.lifetime.start.p0i8(i64 64, i8* %[[bitcastl]])
//
// First, memset `l` to 0.
// CHECK-O3-NEXT: %[[bitcastl:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[l]] to i8*
// CHECK-O3-NEXT: call void @llvm.memset.p0i8.i64(i8* align 8 %[[bitcastl]], i8 0, i64 64, i1 false)
//
// Lifetime of the first temporary starts here and ends right after the call.
// CHECK-O3-NEXT: %[[bitcastbyvaltemp:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[byvaltemp]] to i8*
// CHECK-O3-NEXT: call void @llvm.lifetime.start.p0i8(i64 64, i8* %[[bitcastbyvaltemp]])
//
// Then, memcpy `l` to the temporary stack space.
// CHECK-O3-NEXT: %[[src:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[byvaltemp]] to i8*
// CHECK-O3-NEXT: %[[dst:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[l]] to i8*
// CHECK-O3-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[src]], i8* align 8 %[[dst]], i64 64, i1 false)
// Finally, call using a pointer to the temporary stack space.
// CHECK-O3-NEXT: call void @pass_large(%struct.large* %[[byvaltemp]])
//
// The lifetime of the temporary used to pass a pointer to the struct ends here.
// CHECK-O3-NEXT: %[[bitcastbyvaltemp:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[byvaltemp]] to i8*
// CHECK-O3-NEXT: call void @llvm.lifetime.end.p0i8(i64 64, i8* %[[bitcastbyvaltemp]])
//
// Now, do the same for the second call, using the second temporary alloca.
// CHECK-O3-NEXT: %[[bitcastbyvaltemp:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[byvaltemp1]] to i8*
// CHECK-O3-NEXT: call void @llvm.lifetime.start.p0i8(i64 64, i8* %[[bitcastbyvaltemp]])
// CHECK-O3-NEXT: %[[src:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[byvaltemp1]] to i8*
// CHECK-O3-NEXT: %[[dst:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[l]] to i8*
// CHECK-O3-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %[[src]], i8* align 8 %[[dst]], i64 64, i1 false)
// CHECK-O3-NEXT: call void @pass_large(%struct.large* %[[byvaltemp1]])
// CHECK-O3-NEXT: %[[bitcastbyvaltemp:[0-9A-Za-z-]+]] = bitcast %struct.large* %[[byvaltemp1]] to i8*
// CHECK-O3-NEXT: call void @llvm.lifetime.end.p0i8(i64 64, i8* %[[bitcastbyvaltemp]])
//
// Mark the end of the lifetime of `l`.
// CHECK-O3-NEXT: %[[bitcastl:[0-9A-Za-z-]+]] = bitcast %struct.large* %l to i8*
// CHECK-O3-NEXT: call void @llvm.lifetime.end.p0i8(i64 64, i8* %[[bitcastl]])
// CHECK-O3-NEXT: ret void
44 changes: 44 additions & 0 deletions unittests/Lex/DependencyDirectivesSourceMinimizerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,50 @@ TEST(MinimizeSourceToDependencyDirectivesTest, PragmaOnce) {
EXPECT_STREQ("#pragma once\n#include <test.h>\n", Out.data());
}

TEST(MinimizeSourceToDependencyDirectivesTest,
SkipLineStringCharLiteralsUntilNewline) {
SmallVector<char, 128> Out;

StringRef Source = R"(#if NEVER_ENABLED
#define why(fmt, ...) #error don't try me
#endif
void foo();
)";
ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out));
EXPECT_STREQ(
"#if NEVER_ENABLED\n#define why(fmt,...) #error don't try me\n#endif\n",
Out.data());

Source = R"(#if NEVER_ENABLED
#define why(fmt, ...) "quote dropped
#endif
void foo();
)";
ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out));
EXPECT_STREQ(
"#if NEVER_ENABLED\n#define why(fmt,...) \"quote dropped\n#endif\n",
Out.data());
}

TEST(MinimizeSourceToDependencyDirectivesTest,
SupportWhitespaceBeforeLineContinuationInStringSkipping) {
SmallVector<char, 128> Out;

StringRef Source = "#define X '\\ \t\nx'\nvoid foo() {}";
ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out));
EXPECT_STREQ("#define X '\\ \t\nx'\n", Out.data());

Source = "#define X \"\\ \r\nx\"\nvoid foo() {}";
ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out));
EXPECT_STREQ("#define X \"\\ \r\nx\"\n", Out.data());

Source = "#define X \"\\ \r\nx\n#include <x>\n";
ASSERT_FALSE(minimizeSourceToDependencyDirectives(Source, Out));
EXPECT_STREQ("#define X \"\\ \r\nx\n#include <x>\n", Out.data());
}

TEST(MinimizeSourceToDependencyDirectivesTest, CxxModules) {
SmallVector<char, 128> Out;
SmallVector<Token, 4> Tokens;
Expand Down

0 comments on commit 1cf6569

Please sign in to comment.