> On Dec 13, 2017, at 8:35 PM, Saleem Abdulrasool <compn...@compnerd.org> wrote:
> 
> On Wed, Dec 13, 2017 at 4:14 PM, John McCall <rjmcc...@apple.com 
> <mailto:rjmcc...@apple.com>> wrote:
> 
>> On Dec 13, 2017, at 6:42 PM, Saleem Abdulrasool <compn...@compnerd.org 
>> <mailto:compn...@compnerd.org>> wrote:
>> 
>> 
>> 
>>> On Dec 13, 2017, at 12:46 PM, John McCall <rjmcc...@apple.com 
>>> <mailto:rjmcc...@apple.com>> wrote:
>>> 
>>>> 
>>>> On Dec 13, 2017, at 3:22 PM, Saleem Abdulrasool <compn...@compnerd.org 
>>>> <mailto:compn...@compnerd.org>> wrote:
>>>> 
>>>> 
>>>> 
>>>>> On Dec 13, 2017, at 12:14 PM, John McCall <rjmcc...@apple.com 
>>>>> <mailto:rjmcc...@apple.com>> wrote:
>>>>> 
>>>>>> 
>>>>>> On Dec 13, 2017, at 2:56 PM, Saleem Abdulrasool <compn...@compnerd.org 
>>>>>> <mailto:compn...@compnerd.org>> wrote:
>>>>>> 
>>>>>> Hey guys,
>>>>>> 
>>>>>> I have another fun case that things go wrong in :-).  We do not preserve 
>>>>>> the calling convention information when importing function decls via the 
>>>>>> ClangImporter.  I have a pretty simple example in apple/swift#13404.  
>>>>>> Importing the following:
>>>>>> 
>>>>>>> float frand(void);
>>>>>>> float fadd(float f, float g) { return f + g; }
>>>>>> 
>>>>>> and using this in swift as:
>>>>>> 
>>>>>>> func f -> Float { return fadd(frand(), frand()); }
>>>>>> 
>>>>>> 
>>>>>> will result in the fadd call being elided due to the UB in the CC 
>>>>>> mismatch.  Im pretty sure that we should be preserving CC information 
>>>>>> when importing the interface, probably in `VisitFunctionDecl` in 
>>>>>> ImportDecl.cpp (although, I believe it can also be lazily computed).  Im 
>>>>>> not sure which really would be the best thing to do here.
>>>>>> 
>>>>>> This can show up on other targets when interfaces uses 
>>>>>> `__attribute__((__pcs__))` or `__attribute__((__fastcall__))`, 
>>>>>> `__attribute__((__vectorcall__))`, 
>>>>>> `__attribute__((__regparm__([1-3])))`, `__attribute__((__stdcall__))`, 
>>>>>> `__attribute__((__thiscall__))`.
>>>>> 
>>>>> Without enhancing SIL, I'm not sure we have a good option besides 
>>>>> refusing to import function declarations with non-standard CCs.
>>>>> 
>>>>> I don't think we really want to change AST function types to handle 
>>>>> arbitrary imported calling conventions, but we could change 
>>>>> SILFunctionType to be able to store a Clang CC. However, this will 
>>>>> require a little bit of extra work in that, if someone tries to pass 
>>>>> around the address of a fastcall function as a @convention(c) function, 
>>>>> we will have to introduce a thunk.  I believe we already do similar kinds 
>>>>> of thunking in SILGen, though.
>>>> 
>>>> This is slightly problematic as Linux ARM HF and Windows ARM both use a 
>>>> non-C default (arm_aapcs_vfpcc) which is the test case in the mentioned 
>>>> PR.  That is, even without the attributes the declarations above have a 
>>>> non-C CC.
>>>> 
>>>> I don't think that we are supporting arbitrary calling conventions per se. 
>>>>  This only becomes a problem at the FFI layer, where we want to convert 
>>>> the swift CC to the foreign CC.  The IRGen at that point will generate UB 
>>>> which will truncate the implementation when the LLVM optimizer runs.  I 
>>>> think that we should at least support AAPCS in both, the standard and VFP, 
>>>> variants at the very least.
>>> 
>>> Supporting two C calling conventions is not easier than supporting 
>>> arbitrary C calling conventions.  All the complexity is in how and where we 
>>> represent those CCs.  I'm hesitant to introduce this complexity (and 
>>> non-portability) into the core language, which is why I'm suggesting just 
>>> introducing it into SIL.  But I explicitly do not want us to hard-code 
>>> specific non-standard conventions from C outside of maybe attribute 
>>> parsing; we should generalize the representation to support arbitrary CCs.  
>>> I don't think this is hard at the SIL level.
>> 
>> Hmm, I think that we might be looking at different problems.  I want to 
>> support the de facto standard CC on the target.  If you consider Windows 
>> ARM, “c” is entirely invalid.
> 
> @convention(c) cannot be "entirely invalid".  @convention(c) means the 
> platform's C calling convention, which I promise does always exist.  The code 
> "int foo(void); int main() { foo(); }" does actually compile on Windows ARM, 
> and it uses a calling convention; that is the platform's C calling convention.
> 
> Ah, I was interpreting it to be equivalent to LLVM's notion of the C calling 
> convention which is not the same as you describe it.  On Windows ARM, the C 
> convention requires an additional annotation of the function call.
>  
>> The equivalent of “c” is arm_aapcs_vfpcc.  However, there are other cases 
>> where we do need a secondary CC.  As an example of that, the AEABI RT 
>> function calls are always made as AAPCS, but on Linux ARM HF targets (e.g. 
>> armv7-unknown-linux-gnueabihf), the default CC is the VFP variant of AAPCS, 
>> so we do need a secondary CC there for some library calls.  We usually get 
>> lucky as the function calls there are formed by the backend which knows the 
>> CC necessary for the call there.
> 
> I think you might be confusing "the C calling convention" with some other 
> concept — maybe the default LLVM calling convention?  I believe it's true 
> that LLVM has backends, notably ARM, where the maintainers made the (IMO 
> questionable) decision to have the default LLVM CC be the same across 
> operating systems instead of considering the full target triple.  That just 
> means that frontends have an obligation to set the target's actual CC on 
> every C call and function.  I would readily believe that Swift IRGen has bugs 
> — maybe even pervasive ones — where we fail to do that.
> 
> Yes, this is the scenario that I was describing - the need for the frontend 
> to annotate the call with he correct calling convention for the target (which 
> is equivalent to swift's notion of `@convention(c)`).  However, the CC 
> annotation is entirely missing even for uses of the declarations as the 
> information is not preserved during the import stage.

It's not in the Clang AST, either.  Clang function types only carry a calling 
convention when they're explicitly using a non-standard convention.  This is 
all fine except that we shouldn't just be ignoring non-standard conventions in 
the importer.
 
>>> In general, the importer has a lot of flexibility when importing 
>>> declarations and very little flexibility when importing types, especially 
>>> pointer types.  We can only import one kind of function pointer type as a 
>>> @convention(c) function type; everything else needs to be imported as an 
>>> opaque pointer unless we actually bite the bullet and enhance the AST 
>>> @convention system to support more C conventions.  For maximum 
>>> expressivity, that function pointer type needs to be the type most commonly 
>>> used for function pointers on the platform, assuming there is one.  It 
>>> doesn't really matter if that's not the default calling convention for 
>>> function *declarations* because we can always pass around a specific 
>>> variant-CC function as a @convention(c) function value by introducing a 
>>> thunk; function-pointer equality won't work, but probably nobody cares.  
>>> What we can't do is turn an arbitrary variant-CC function *pointer* into a 
>>> @convention(c) function pointer.  But the correctness of all this relies on 
>>> SIL being able to fully represent the C calling convention.
>> 
>> In the case of support of a target which does not support the C calling 
>> convention but expects all calls to be of a specific convention, we 
>> currently fall apart.  It sounds like you would prefer that the approach to 
>> handle that would be to map `@convention(c)` to that alternate calling 
>> convention?  For importing declarations, we currently do not preserve the 
>> calling convention at all.  As a result, right now, FFI calls into C may 
>> introduce UB in the IR.
> 
> Yes, we clearly have a responsibility to call things with the right calling 
> convention, and we should not import types and declarations which we cannot 
> meet that responsibility for.  I am trying to describe the correct technical 
> design for that, which is:
> 
> Great, so we agree on this :-).
>  
>   - In the importer, we do not need to assign a specific convention to the 
> type of an imported function declaration.
> 
> I don't understand how this works when we may need to annotate the function 
> call to match the C calling convention.

SIL knows how to map a Swift declaration back to a Clang declaration and use 
its type to guide SIL type lowering.  There's a somewhat obscure set of 
assumptions at work here.
 
>   - In the importer, we should import function pointer types as 
> @convention(c) function types if they use the target's C calling convention 
> and as opaque pointer types otherwise.
> 
> Fair enough.
>  
>   - SILFunctionType should have the ability to express a calling convention 
> more precisely than just @convention(c).  IRGen can just use this value when 
> explicit.  For @convention(c), IRGen can ask Clang for the correct LLVM CC to 
> use.
> 
> Agreed that the calls should be able to declare this more precisely.  Do we 
> want to round trip back to clang to get the LLVM CC for this call?  
> Especially if this is the "C" CC?

SILFunctionType should carry the Clang CC.  When Swift IRGen asks Clang how to 
lower a function call/declaration, Clang will tell it what LLVM CC to use; it's 
one of the fields on CGFunctionInfo.
 
>   - SIL type lowering should assign function declarations their actual C 
> calling convention if they use a non-standard calling convention.
> 
> Yes.
>  
>   - SILGen may need to introduce thunks when passing around such functions as 
> @convention(c) function values.  Code for this already exists in order to 
> allow C functions to be passed around as native function values.
> 
> This sounds pretty good and would broaden the abilities for swift to FFI to 
> existing code.

Yeah.  This should work well as long as there isn't an API that needs to 
traffic in non-standard function *pointers*.

John.

>  
> John.
> 
>> I think that the FFI to declarations is far more common than the FFI to a 
>> function pointer, which is why I am focusing on the imported declaration 
>> rather than the imported types.  I do agree that supporting additional 
>> conventions in the SIL layer would be good for the imported function pointer 
>> case.  Am I missing something and is the `@convention` used for the imported 
>> declarations too?
>> 
>>> John.
> -- 
> Saleem Abdulrasool
> compnerd (at) compnerd (dot) org

_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Reply via email to