Skip to content
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

More complete support for opaque pointers (required for LLVM 15+) #221

Merged
merged 14 commits into from
May 30, 2023
Merged
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
4 changes: 3 additions & 1 deletion .github/workflows/llvm-quick-fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ jobs:
os: [ubuntu-22.04]
# See doc/developing.md
ghc: ["8.10.7"]
llvm: [ "https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
llvm: [ "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.2/clang+llvm-16.0.2-x86_64-linux-gnu-ubuntu-22.04.tar.xz"
, "https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
, "https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
, "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang+llvm-13.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz"
, "https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/clang+llvm-12.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz"
, "https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ compilers.
| | v12.0 | ✓ | | |
| | v13.0 | ✓ | | See [issues][llvm13] |
| | v14.0 | ✓ | | See [issues][llvm14] |
| | v15.0 | | | See [issues][llvm15] |
| | v16.0 | | | See [issues][llvm16] |
| | v15.0 | | | See [issues][llvm15] |
| | v16.0 | | | See [issues][llvm16] |
| `clang++` | v3.4 | | | |
| | v3.5 | | | |
| | v3.6 | | | |
Expand Down
6 changes: 4 additions & 2 deletions disasm-test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ cube = TS.mkCUBE
, "pre-llvm12"
, "pre-llvm13"
, "pre-llvm14"
, "pre-llvm15"
])
]
-- Somewhat unusually for tasty-sugar, we make the expectedSuffix the same
Expand Down Expand Up @@ -366,11 +367,12 @@ processBitCode _keep (Roundtrip roundtrip) pfx file = do
case e of
Left err -> X.throwIO (ParseError err)
Right m -> do
parsed <- printToTempFile "ll" (show (ppLLVM (ppModule m)))
let m' = AST.fixupOpaquePtrs m
parsed <- printToTempFile "ll" (show (ppLLVM (ppModule m')))
-- stripComments _keep parsed
if roundtrip
then do
tmp2 <- printToTempFile "ast" (ppShow (normalizeModule m))
tmp2 <- printToTempFile "ast" (ppShow (normalizeModule m'))
return (parsed, Just tmp2)
else return (parsed, Nothing)

Expand Down
18 changes: 18 additions & 0 deletions disasm-test/tests/callbr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Adapted from the Clang test suite:
// (https://github.com/llvm/llvm-project/blob/32103608fc073700f04238872d8b22655ddec3fb/clang/test/CodeGen/asm-goto.c#L5-L20),
// which is licensed under the Apache License v2.0


int main(void) {
int cond = 0;
asm volatile goto("testl %0, %0; jne %l1;"
: /* No outputs */
: "r"(cond)
: /* No clobbers */
: label_true, loop);
return 0;
loop:
return 0;
label_true:
return 1;
}
67 changes: 37 additions & 30 deletions disasm-test/tests/callbr.ll
Original file line number Diff line number Diff line change
@@ -1,40 +1,47 @@
; ModuleID = 'test.c'
source_filename = "test.c"
; ModuleID = 'callbr.c'
source_filename = "callbr.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @test1(i32 %0) #0 {
define dso_local i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = alloca i32, align 4
store i32 %0, i32* %3, align 4
%4 = load i32, i32* %3, align 4
callbr void asm sideeffect "testl $0, $0; jne ${1:l};", "r,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %4, i8* blockaddress(@test1, %7), i8* blockaddress(@test1, %6)) #1
to label %5 [label %7, label %6], !srcloc !2

5: ; preds = %1
store i32 0, i32* %2, align 4
br label %8

6: ; preds = %1
store i32 0, i32* %2, align 4
br label %8

7: ; preds = %1
store i32 1, i32* %2, align 4
br label %8

8: ; preds = %7, %6, %5
%9 = load i32, i32* %2, align 4
ret i32 %9
store i32 0, ptr %1, align 4
store i32 0, ptr %2, align 4
%3 = load i32, ptr %2, align 4
callbr void asm sideeffect "testl $0, $0; jne ${1:l};", "r,!i,!i,~{dirflag},~{fpsr},~{flags}"(i32 %3) #1
to label %4 [label %6, label %5], !srcloc !7

4: ; preds = %0
store i32 0, ptr %1, align 4
br label %7

5: ; preds = %0
store i32 0, ptr %1, align 4
br label %7

6: ; preds = %0
store i32 1, ptr %1, align 4
br label %7

7: ; preds = %6, %5, %4
%8 = load i32, ptr %1, align 4
ret i32 %8
}

attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { nounwind }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}
!llvm.commandline = !{!6}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 10.0.0-4ubuntu1 "}
!2 = !{i32 265}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"clang version 15.0.6"}
!6 = !{!"/home/ryanglscott/Software/clang+llvm-15.0.6/bin/clang-15 -S -emit-llvm -frecord-command-line callbr.c -o callbr.ll"}
!7 = !{i64 272}
40 changes: 40 additions & 0 deletions disasm-test/tests/callbr.pre-llvm15.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @test1(i32 %0) #0 {
%2 = alloca i32, align 4
%3 = alloca i32, align 4
store i32 %0, i32* %3, align 4
%4 = load i32, i32* %3, align 4
callbr void asm sideeffect "testl $0, $0; jne ${1:l};", "r,X,X,~{dirflag},~{fpsr},~{flags}"(i32 %4, i8* blockaddress(@test1, %7), i8* blockaddress(@test1, %6)) #1
to label %5 [label %7, label %6], !srcloc !2

5: ; preds = %1
store i32 0, i32* %2, align 4
br label %8

6: ; preds = %1
store i32 0, i32* %2, align 4
br label %8

7: ; preds = %1
store i32 1, i32* %2, align 4
br label %8

8: ; preds = %7, %6, %5
%9 = load i32, i32* %2, align 4
ret i32 %9
}

attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 10.0.0-4ubuntu1 "}
!2 = !{i32 265}
4 changes: 4 additions & 0 deletions disasm-test/tests/opaque-atomicrmw.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
define void @atomicrmw(ptr %a, i32 %i) {
%b = atomicrmw add ptr %a, i32 %i acquire
ret void
}
4 changes: 4 additions & 0 deletions disasm-test/tests/opaque-atomicrmw.pre-llvm15.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SKIP_TEST

This test case requires the use of opaque pointers, which are most easily
usable with LLVM 15 or later.
11 changes: 11 additions & 0 deletions disasm-test/tests/opaque-call.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
define void @f(i32 %x) {
ret void
}

define void @g() {
%p = alloca ptr
store ptr @f, ptr %p
%f = load ptr, ptr %p
call void (i32) %f(i32 42)
ret void
}
4 changes: 4 additions & 0 deletions disasm-test/tests/opaque-call.pre-llvm15.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SKIP_TEST

This test case requires the use of opaque pointers, which are most easily
usable with LLVM 15 or later.
9 changes: 9 additions & 0 deletions disasm-test/tests/opaque-constant-getelementptr.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
%struct.RT = type { i8, [10 x [20 x i32]], i8 }
%struct.ST = type { i32, double, %struct.RT }

@.s = private constant %struct.ST zeroinitializer

define ptr @foo() {
entry:
ret ptr getelementptr inbounds (%struct.ST, ptr @.s, i64 1, i32 2, i32 1, i64 5, i64 13)
}
4 changes: 4 additions & 0 deletions disasm-test/tests/opaque-constant-getelementptr.pre-llvm15.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SKIP_TEST

This test case requires the use of opaque pointers, which are most easily
usable with LLVM 15 or later.
8 changes: 8 additions & 0 deletions disasm-test/tests/opaque-getelementptr.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
%struct.RT = type { i8, [10 x [20 x i32]], i8 }
%struct.ST = type { i32, double, %struct.RT }

define ptr @foo(ptr %s) {
entry:
%arrayidx = getelementptr inbounds %struct.ST, ptr %s, i64 1, i32 2, i32 1, i64 5, i64 13
ret ptr %arrayidx
}
4 changes: 4 additions & 0 deletions disasm-test/tests/opaque-getelementptr.pre-llvm15.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SKIP_TEST

This test case requires the use of opaque pointers, which are most easily
usable with LLVM 15 or later.
2 changes: 1 addition & 1 deletion llvm-pretty
2 changes: 1 addition & 1 deletion llvm-pretty-bc-parser.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Test-suite disasm-test
tasty-hunit,
tasty-sugar >= 2.2 && < 2.3,
text,
versions < 6,
versions < 7,
llvm-pretty,
llvm-pretty-bc-parser

Expand Down
57 changes: 25 additions & 32 deletions src/Data/LLVM/BitCode/Assert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ module Data.LLVM.BitCode.Assert
-- ** Types
, elimPtrTo
, elimPtrTo_
, ptrTo
RyanGlScott marked this conversation as resolved.
Show resolved Hide resolved
) where

import Control.Monad (MonadPlus, mplus)
Expand All @@ -32,7 +31,7 @@ import Control.Monad.Fail (MonadFail)
#endif
import Data.LLVM.BitCode.Record (Record)
import qualified Data.LLVM.BitCode.Record as Record
import Text.LLVM.AST (Type', Typed, Ident)
import Text.LLVM.AST (Type', Ident)
import qualified Text.LLVM.AST as AST

supportedCompilerMessage :: [String]
Expand Down Expand Up @@ -80,43 +79,37 @@ recordSizeIn record ns =
----------------------------------------------------------------
-- ** Types

-- | Assert that this thing is a pointer, get the underlying type
-- | Assert that this thing is a @'PtrTo' ty@ and return the underlying @ty@.
--
-- Think carefully before using this function, as it will not work as you would
-- expect when the type is an opaque pointer.
-- See @Note [Pointers and pointee types]@.
RyanGlScott marked this conversation as resolved.
Show resolved Hide resolved
elimPtrTo :: (MonadFail m, MonadPlus m) => String -> Type' Ident -> m (Type' Ident)
elimPtrTo msg ptrTy = AST.elimPtrTo ptrTy `mplus`
(fail $ unlines [ msg
, "Expected pointer type, found:"
, show ptrTy
])

-- | Assert that this thing is a pointer
-- | Assert that this thing is a 'PtrTo' type.
--
-- Think carefully before using this function, as it will not work as you would
-- expect when the type is an opaque pointer.
-- See @Note [Pointers and pointee types]@.
elimPtrTo_ :: (MonadFail m, MonadPlus m) => String -> Type' Ident -> m ()
elimPtrTo_ msg ptrTy = elimPtrTo msg ptrTy >> pure ()

-- | Assert that the first thing is a pointer to something of the type of the
-- second thing, e.g. in a load/store instruction.
--
-- See: https://github.com/llvm-mirror/llvm/blob/release_60/lib/Bitcode/Reader/BitcodeReader.cpp#L3328
ptrTo :: (MonadFail m, Show a, Show b)
=> String
-> Typed a -- ^ The pointer
-> Typed b -- ^ The value
-> m ()
ptrTo sig ptr val = do
case AST.typedType ptr of
AST.PtrTo ptrTo_ ->
when (AST.typedType val /= ptrTo_) $ fail $ unlines
[ unwords [ "Expected first value to be a pointer to some type <ty>, and"
, "for the second value to be a value of type <ty>."
]
, "Instruction signature: " ++ sig
, "Pointer type: " ++ show (AST.typedType ptr)
, "Value type: " ++ show (AST.typedType val)
, "Pointer value: " ++ show (AST.typedValue ptr)
, "Value value: " ++ show (AST.typedValue val)
]
ty ->
fail $ unlines $
[ "Instruction expected a pointer argument."
, "Instruction signature: " ++ sig
, "Argument type: " ++ show ty
]
{-
Note [Pointers and pointee types]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unlike LLVM itself, llvm-pretty and llvm-pretty-bc-parser allow mixing opaque
and non-opaque pointers. A consequence of this is that we generally avoid
pattern matching on PtrTo (non-opaque pointer) types and inspecting the
underlying pointee types. This sort of code simply won't work for PtrOpaque
types, which lack pointee types.

The elimPtrTo and elimPtrTo_ functions go against this rule, as they retrieve
the pointee type in a PtrTo. These functions are primarily used for supporting
old versions of LLVM which do not store the necessary type information in the
instruction itself.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a recommendation for what to use instead for opaque pointers and more modern approaches?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

elimPtrTo is typically used in situations in which an explicit Type is needed, but the only way to retrieve the Type is by inspecting a pointer type. Because opaque pointers don't have pointee types, there is no direct elimPtrTo counterpart for opaque pointers. The modern approach would be to store the relevant Type information nearby so that it can always be retrieved independently of the pointer. There isn't an exact template that you can follow for this, but in the case of LLVM instructions, this is usually tantamount to having an extra Type field alongside the Value representing the pointer.

-}
Loading
Loading