RefineAbstractType

Is there some reason that RefineAbstractType doesn't recurse down DerivedTypes
and further resolve abstract types?

In Fortran it is possible to have a routine that takes a function pointer to a
routine with the same signature. This is not possible to express in C/C++.
I'm not sure it's possible to express in LLVM either without using varargs
(opinions welcome).

In a simpler C++ example, we end upcreating a function pointer type:

void (...) * (opaque *)

We want to resolve "opaque" to be

void (...)

Note that this isn't the same issue as in Fortran (it's not a recursive type)
but it serves the purpose for this question.

Unfortunately, calling FunctionType::refineAbstractType(opaque, void (...))
doesn't work because RefineAbstractType doesn't recurse down into the
pointee types. It ends up giving back

void (...) * (opaque *)

so it didn't resolve anything.

It's not clear how to fix this without a core LLVM change because the pointee
depth could be arbitrary (pointer to pointer to pointer...).

Thoughts?

                                           -Dave

Is there some reason that RefineAbstractType doesn't recurse down DerivedTypes
and further resolve abstract types?

Because that would resolve two different types. RefineAbstractType just refines the one type you request, and further uniques types based on it.

In fact, I strongly suspect that RefineAbstractType doesn't work if you give it something that isn't Opaque. I'd recommend only using it on Opaque, not random abstract types.

In Fortran it is possible to have a routine that takes a function pointer to a
routine with the same signature. This is not possible to express in C/C++.
I'm not sure it's possible to express in LLVM either without using varargs
(opinions welcome).

Sure, LLVM is more general than C. In LLVM, you can have a pointer that points to itself, for example:

%t = type %t*

which is "\1*" if you like up references.

I'm not sure what a function pointer that takes a pointer to itself has to do with opaque types, but you'd write it like this:

%t = type void (%t)*

or if you want the function:

%t = type void (%t*)

Unfortunately, calling FunctionType::refineAbstractType(opaque, void (...))
doesn't work because RefineAbstractType doesn't recurse down into the
pointee types.

What types are you starting out with? If you had the equivalent of:

%t1 = type opaque
%t2 = type void(%t1*)

and you refine t1 -> t2, it should work.

-Chris

In fact, I strongly suspect that RefineAbstractType doesn't work if
you give it something that isn't Opaque. I'd recommend only using it
on Opaque, not random abstract types.

Ok.

Sure, LLVM is more general than C. In LLVM, you can have a pointer
that points to itself, for example:

%t = type %t*

which is "\1*" if you like up references.

I'm not sure what a function pointer that takes a pointer to itself
has to do with opaque types, but you'd write it like this:

It has to do with the way we're processing things right now. We have to
break the processing recursion so we create an Opaque type as a stand-in
for the (yet-to-be-completed) FunctionType when we encounter an argument
referencing the (yet-to-be-completed) FunctionType. We want to resolve those
OpaqueTypes once we have the FunctionType constructed.

%t = type void (%t)*

or if you want the function:

%t = type void (%t*)

Cool.

> Unfortunately, calling FunctionType::refineAbstractType(opaque, void
> (...))
> doesn't work because RefineAbstractType doesn't recurse down into the
> pointee types.

What types are you starting out with? If you had the equivalent of:

%t1 = type opaque
%t2 = type void(%t1*)

and you refine t1 -> t2, it should work.

I'm not sure it will. %t1* is the "contained" type inside the FunctionType.
RefineAbstractTypes expects the OldType (%t1 in this case) to be in the
contained type set. Since a _pointer_ to %t1 is in the contained set, it
doesn't see it.

                                                         -Dave

How is this any different than the struct case of {\2*} ?

-Chris

I don't know. All I know is when I called %t2->refineAbstractType(%t1, %t2)
it didn't work. That was the pointer of the question. RefineAbstractTypes
doesn't "see" beyond the pointer type.

I think the answer is that I have to resolve the pointer type, not the
function type:

%t1 = type opaque
%t1.5 = type %t1 *
%t2 = type void(%t1.5)

%t1.5->refineAbstractType(%t1, %t2)

                                                 -Dave

You shouldn't be refining the pointer, you should use:

t1->refineAbstractType(t2)

-Chris

Oh, I see.

Then what's the purpose of PointerType::refineAbstractType,
FunctionType::refineAbstractType, etc.?

                                                -Dave

They are "internal" implementation details of the refinement system.

-Chris

Ok, I tried this and I get further. But the type system seems to have trouble
when refining multiple types and those types resolve to the same thing.
I turned on DEBUG_MERGE_TYPES to illustrate:

*** First function type ***

TypeMap<>::add table contents:
1. 0x3a4e6a0 i32 *
Derived new type: i32 *
TypeMap<>::add table contents:
1. 0x3a4e910 [1 x i64]
Derived new type: [1 x i64]
TypeMap<>::add table contents:
1. 0x3a4e6a0 i32 *
2. 0x3a4eb60 [1 x i64] *
Derived new type: [1 x i64] *
TypeMap<>::add table contents:
1. 0x3a4e6a0 i32 *
2. 0x3a4f140 i64 *
3. 0x3a4eb60 [1 x i64] *
Derived new type: i64 *
Derived new type: opaque
TypeMap<>::add table contents:
1. 0x3a4e6a0 i32 *
2. 0x3a4f140 i64 *
3. 0x3a4eb60 [1 x i64] *
4. 0x3a8c310 opaque *
Derived new type: opaque *
TypeMap<>::add table contents:
1. 0x3a8c450 i32 (opaque *)
Derived new type: i32 (opaque *)
TypeMap<>::add table contents:
1. 0x3a4e6a0 i32 *
2. 0x3a4f140 i64 *
3. 0x3a4eb60 [1 x i64] *
4. 0x3a8c680 i32 (opaque *) *
5. 0x3a8c310 opaque *
Derived new type: i32 (opaque *) *

*** First refinement ***

REFINING abstract type [0x3afd350 opaque] to [0x3a8c450 i32 (opaque *)]!
REFINING user 0[0x3a8c310] of abstract type [0x3afd350 opaque] to [0x3a8c450
i32 (opaque *)]!
RefineAbstractType(0x3afd350[opaque], 0x3a8c450 [i32 (opaque *)])
  remAbstractTypeUser[0x3afd350, opaque][0] User = 0x3a8c310
REFINING abstract type [0x3a8c310 i32 (\2) *] to [0x3a8c680 i32 (\2 *) *]!
Derived new type: opaque
  remAbstractTypeUser[0x3a8c450, i32 (\2 *)][1] User = 0x3a8c310
REFINING user 0[0x3a8c450] of abstract type [0x3a8c310 i32 (\2) *] to
[0x3a8c680 i32 (\2 *) *]!
RefineAbstractType(0x3a8c310[i32 (\2) *], 0x3a8c680 [i32 (\2 *) *])
  remAbstractTypeUser[0x3a8c310, i32 (\2) *][0] User = 0x3a8c450
typeIsREFINED type: 0x3a8c680 i32 (\2) *
  remAbstractTypeUser[0x3a8c680, i32 (\2) *][0] User = 0x3a8c450
typeIsREFINED type: 0x3a8c450 i32 (i32 (\2) *)
  remAbstractTypeUser[0x3a8c450, i32 (i32 (\2) *)][0] User = 0x3a8c680
  remAbstractTypeUser[0x3a8c810, opaque][0] User = 0x3a8c310
TypeMap<>::add table contents:
1. 0x3a8c310 void ()
2. 0x3a8c450 i32 (i32 (\2) *)

*** Ok, this all looks perfect at this point ***

*** Second function type ***

Derived new type: void ()
TypeMap<>::add table contents:
1. 0x3a4e6a0 i32 *
2. 0x3a4f140 i64 *
3. 0x3a4eb60 [1 x i64] *
4. 0x3a8cb40 void () *
5. 0x3a8c680 i32 (\2) *
Derived new type: void () *
Derived new type: opaque
TypeMap<>::add table contents:
1. 0x3a4e6a0 i32 *
2. 0x3a4f140 i64 *
3. 0x3a4eb60 [1 x i64] *
4. 0x3a8cb40 void () *
5. 0x3a8c680 i32 (\2) *
6. 0x3a8d000 opaque *
Derived new type: opaque *
TypeMap<>::add table contents:
1. 0x3a8c310 void ()
2. 0x3a8c450 i32 (i32 (\2) *)
3. 0x3a8d1c0 i32 (opaque *)
Derived new type: i32 (opaque *)
TypeMap<>::add table contents:
1. 0x3a4e6a0 i32 *
2. 0x3a4f140 i64 *
3. 0x3a4eb60 [1 x i64] *
4. 0x3a8cb40 void () *
5. 0x3a8c680 i32 (\2) *
6. 0x3a8d420 i32 (opaque *) *
7. 0x3a8d000 opaque *
Derived new type: i32 (opaque *) *

*** Second refinement -- note that it's the same as the first refinement,
even "opaque" has the same address! That seems suspicious to me. ***

REFINING abstract type [0x3afd350 opaque] to [0x3a8d1c0 i32 (opaque *)]!
REFINING user 0[0x3a8d000] of abstract type [0x3afd350 opaque] to [0x3a8d1c0
i32 (opaque *)]!
RefineAbstractType(0x3afd350[opaque], 0x3a8d1c0 [i32 (opaque *)])
  remAbstractTypeUser[0x3afd350, opaque][0] User = 0x3a8d000
REFINING abstract type [0x3a8d000 i32 (\2) *] to [0x3a8c680 i32 (\2) *]!
  remAbstractTypeUser[0x3a8d1c0, i32 (\2 *)][1] User = 0x3a8d000
REFINING user 0[0x3a8d1c0] of abstract type [0x3a8d000 i32 (\2) *] to
[0x3a8c680 i32 (\2) *]!
RefineAbstractType(0x3a8d000[i32 (\2) *], 0x3a8c680 [i32 (\2) *])
  remAbstractTypeUser[0x3a8d000, i32 (\2) *][0] User = 0x3a8d1c0
REFINING abstract type [0x3a8d1c0 i32 (i32 (\2) *)] to [0x3a8c450 i32 (i32
(\2) *)]!
REFINING user 0[0x3a8d420] of abstract type [0x3a8d1c0 i32 (i32 (\2) *)] to
[0x3a8c450 i32 (i32 (\2) *)]!
RefineAbstractType(0x3a8d1c0[i32 (i32 (\2) *)], 0x3a8c450 [i32 (i32 (\2) *)])
  remAbstractTypeUser[0x3a8d1c0, i32 (i32 (\2) *)][0] User = 0x3a8d420
REFINING abstract type [0x3a8d420 i32 (i32 (\2) *) *] to [0x3a8c680 i32 (\2)
*]!
  remAbstractTypeUser[0x3a8c810, opaque][2] User = 0x3a8d420
  remAbstractTypeUser[0x3a8c810, opaque][0] User = 0x3a8d000

*** BLAMMO! ***

llvm/lib/VMCore/Type.cpp:474: llvm::FunctionType::FunctionType(const
llvm::Type*, const std::vector<const llvm::Type*, std::allocator<const
llvm::Type*> >&, bool): Assertion `(Params[i]->isFirstClassType() ||
isa<OpaqueType>(Params[i])) && "Function arguments must be value types!"'
failed.
Caught signal 6

Any idea what's happening?

Is it just the case that I shouldn't be doing two refinements that yield the
same type? How do I know in general that that will happen? Right now
I'm refining types at the earliest possible moment, as we go along
processing the code. Is there a requirement to only refine after all types
have been created? I can't wait until all of the code is seen in general
because we need to dump asm after processing each function -- we
can't wait until we've seen all functions (=> we can't wait until we've
seen all types to refine).

                                                -Dave

Then why make them public? Shouldn't the user be a friend of PointerType,
etc.?

Yeah, I hate C++'s limited access control expressivity too. :slight_smile:

                                                    -Dave

Attached is some code that illustrates what I think is the problem.

Compile with
g++ -D__STDC_LIMIT_MACROS -I<path to LLVM headers> -L<path to LLVM libraries>
-lLLVMCore -lLLVMSupport -lLLVMSystem -ldl

(I built with LLVM 2.4)

Output:

i32 (\1)
opaque (i32)

This looks totally wrong. Am I using refineAbstractTypeTo incorrectly?

I think what's happening is the internal type confusion exhibited in the
output above is propagating through our compiler and eventually causes
bad stuff to happen. When I step through in the debugger I see weird
unrelated types like the opaque (i32) above.

                                               -Dave

refine.cc (983 Bytes)

Any opinions as to whether this is a bug? Should I file a PR?

                                               -Dave

Silly me. I am using it incorrectly. I found the bit of manual that talks
about PATypeHolder.

Sorry for the waste of bandwidth.

                                                         -Dave