GaloisInc / mir-json

Plugin for rustc to dump MIR in JSON format
Apache License 2.0
9 stars 2 forks source link

Support `extern` functions #61

Open RyanGlScott opened 11 months ago

RyanGlScott commented 11 months ago

If you compile an extern function with mir-json, e.g.,

#[link(name = "my_c_library")]
extern "C" {
    pub fn my_c_function(x: i32) -> bool;
}

Then it will not appear in the resulting MIR JSON file:

$ saw-rustc test.rs 
$ jq . test.linked-mir.json
{
  "fns": [],
  "adts": [],
  "statics": [],
  "vtables": [],
  "traits": [],
  "intrinsics": [],
  "tys": [],
  "roots": []
}

This makes it impossible to use with SAW's mir_unsafe_assume_spec command. We should at least emit something for extern functions, even though they have no bodies.


Debugging notes: If you call my_c_function from another function, e.g.,

#[link(name = "my_c_library")]
extern "C" {
    pub fn my_c_function(x: i32) -> bool;
}

pub fn foo(x: i32) -> bool {
    unsafe { my_c_function(x) }
}

Then compiling it with mir-json still won't give you something that can be looked up with mir_unsafe_assume_spec, since there won't be an entry for my_c_function in fns. There will, on the other hand, be some information related to my_c_function in the MIR JSON file:

{
  ...,
  "intrinsics": [
    ...,
    {
      "inst": {
        "def_id": "test/7570fc8a::{extern#0}::my_c_function",
        "kind": "Item",
        "substs": []
      },
      "name": "test/7570fc8a::{extern#0}::my_c_function"
    },
  "tys": [
    ...,
    {
      "name": "ty::FnDef::347ebd14a4cf7039",
      "ty": {
        "defid": "test/7570fc8a::{extern#0}::my_c_function",
        "kind": "FnDef"
      }
    }
}

This is better than nothing, but ultimately, mir_unsafe_assume_spec needs to be able to look up a function's type signature, which isn't easily accessible with this information alone. Question: should we put extern functions in fns (with no body), or should we put them in an entirely separate part of the MIR JSON file?

Note that unlike bugs such as #57, this one is not caused by missing a case in init_instances_from_tests, as that function only considers things that mir_keys() returns. Mostly, these are things with bodies of some sort, and as far as I can tell, this entirely excludes extern functions. In order to include extern functions in the MIR JSON, we will likely need to use a different rustc_middle API function to query them—foreign_modules(), perhaps?

spernsteiner commented 11 months ago

Question: should we put extern functions in fns (with no body), or should we put them in an entirely separate part of the MIR JSON file?

Looking at the current json format for fns, the name, return_ty, and abi fields all apply to extern fns, args partially applies (it has the argument types, but also details like name and mutability that aren't relevant to callers), and body and spread_arg don't apply. If we do end up adding a separate extern_fns table to the JSON format, it might be a good idea to extract all the caller-relevant info (arg types, return_ty, abi, and maybe spread_arg) into a common "fn sig" object that has the same format in both fns and extern_fns.

that function only considers things that mir_keys() returns. Mostly, these are things with bodies of some sort, and as far as I can tell, this entirely excludes extern functions

Right - extern fns have no executable rust code, so no MIR and no "MIR key".

order to include extern functions in the MIR JSON, we will likely need to use a different rustc_middle API function to query them—foreign_modules(), perhaps?

That would work. Another option would be to refactor init_instances_from_tests to explicitly visit every item in the crate with something like TyCtxt::hir_crate_items, which would make it clearer which defs we do and don't export, as opposed to the current approach that implicitly relies on some knowledge about which kinds of items have MIR.