At the most primitive level, with a closed universe of types and values defined in the libraries, we can build a basic interpreter (a giant switch table) and process an expression into a call graph and evaluate the expression. The whole thing may be compiled into a native executable because all types are know at compile time. If the number of types is small enough the table may not even be that big and the whole thing may be tolerable.
Next we may be able to introduce bindings to hold intermediate values so we need a top level env that can be modified. So long as we can only bind with the results of function applications (we can only beta reduce; no abstractions) there are only minor changes from the first.
Now if we want to be able to define new functions we get to the realm of a full OCaml toplevel. The toplevel compiles the code into bytes and uses a runtime. Problem is the runtime has a limited number of primitives and does not know about our libraries at compile time so it can only use byte compiled libraries. So the question is: if we can limit ourselves to fixed libraries what does it take to produce a natively compiled toplevel linked with natively compiled libraries? In a way we could solve the problem by treating the native libraries as foreign language code and go through an FFI or RPC. I am hoping we can do better since both sides are OCaml after all!