From 814dcb0fd6ca0f2d5fb70fcf35a5c29ff2b1a116 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 26 Jan 2023 13:13:05 -0700 Subject: [PATCH 1/5] Hack on stackless translation --- Cargo.lock | 33 +- Cargo.toml | 1 + language/move-model/src/lib.rs | 2 +- .../tools/move-mv-llvm-compiler/Cargo.toml | 4 + .../llvm-extra-sys/Cargo.toml | 13 + .../llvm-extra-sys/build.rs | 66 +++ .../llvm-extra-sys/src/lib.rs | 99 ++++ .../llvm-extra-sys/src/llvm-extra.cpp | 166 +++++++ .../tools/move-mv-llvm-compiler/src/cstr.rs | 40 ++ .../tools/move-mv-llvm-compiler/src/lib.rs | 4 +- .../tools/move-mv-llvm-compiler/src/main.rs | 88 +++- .../src/stackless/llvm.rs | 359 +++++++++++++++ .../src/stackless/mod.rs | 434 ++++++++++++++++++ .../move-mv-llvm-compiler/src/support/mod.rs | 2 +- .../abort-build/modules/0_Test.expected.ll | 17 + .../tests/move-ir-tests/abort.move | 5 + .../assert-build/modules/0_Test.expected.ll | 17 + .../tests/move-ir-tests/assert.move | 5 + .../assign-build/scripts/main.expected.ll | 10 + .../tests/move-ir-tests/assign.move | 5 + .../modules/0_Empty.expected.ll | 8 +- .../eq-build/modules/0_Test.expected.ll | 24 + .../tests/move-ir-tests/eq.move | 5 + .../if-build/modules/0_Test.expected.ll | 36 ++ .../tests/move-ir-tests/if.move | 5 + .../math_u8-build/modules/0_Test.expected.ll | 24 + .../tests/move-ir-tests/math_u8.move | 6 + .../return-build/modules/0_Test.expected.ll | 11 + .../tests/move-ir-tests/return.move | 5 + .../script-build/scripts/main.expected.ll | 12 +- 30 files changed, 1484 insertions(+), 22 deletions(-) create mode 100644 language/tools/move-mv-llvm-compiler/llvm-extra-sys/Cargo.toml create mode 100644 language/tools/move-mv-llvm-compiler/llvm-extra-sys/build.rs create mode 100644 language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/lib.rs create mode 100644 language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/llvm-extra.cpp create mode 100644 language/tools/move-mv-llvm-compiler/src/cstr.rs create mode 100644 language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs create mode 100644 language/tools/move-mv-llvm-compiler/src/stackless/mod.rs create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort.move create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert.move create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign.move create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq.move create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if.move create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8.move create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll create mode 100644 language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return.move diff --git a/Cargo.lock b/Cargo.lock index c6ba55bbf6..4069fcbbc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.52" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "arbitrary" @@ -678,9 +678,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.67" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" dependencies = [ "jobserver", ] @@ -1767,6 +1767,17 @@ dependencies = [ "sha3 0.8.2", ] +[[package]] +name = "extension-trait" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5129068fe3183546eaa0529af88ab0afbcddec2a373db69e94a20b8d5f6c4d74" +dependencies = [ + "proc-macro2 1.0.43", + "quote 1.0.9", + "syn 1.0.99", +] + [[package]] name = "extract-ethereum-abi" version = "0.1.0" @@ -2752,6 +2763,16 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +[[package]] +name = "llvm-extra-sys" +version = "0.1.0" +dependencies = [ + "anyhow", + "cc", + "libc", + "llvm-sys", +] + [[package]] name = "llvm-sys" version = "150.0.3" @@ -3383,7 +3404,9 @@ dependencies = [ "clap 3.1.8", "colored", "datatest-stable", + "extension-trait", "libc", + "llvm-extra-sys", "llvm-sys", "move-binary-format", "move-bytecode-source-map", @@ -3393,6 +3416,8 @@ dependencies = [ "move-core-types", "move-coverage", "move-ir-types", + "move-model", + "move-stackless-bytecode", "once_cell", "parking_lot 0.12.1", "semver 1.0.14", diff --git a/Cargo.toml b/Cargo.toml index 447ec7ba46..931b759348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ members = [ "language/tools/move-disassembler", "language/tools/move-explain", "language/tools/move-mv-llvm-compiler", + "language/tools/move-mv-llvm-compiler/llvm-extra-sys", "language/tools/move-package", "language/tools/move-resource-viewer", "language/tools/move-unit-test", diff --git a/language/move-model/src/lib.rs b/language/move-model/src/lib.rs index f1fe7fda3b..85a9c6bc3d 100644 --- a/language/move-model/src/lib.rs +++ b/language/move-model/src/lib.rs @@ -364,7 +364,7 @@ fn add_move_lang_diagnostics(env: &mut GlobalEnv, diags: Diagnostics) { } #[allow(deprecated)] -fn script_into_module(compiled_script: CompiledScript) -> CompiledModule { +pub fn script_into_module(compiled_script: CompiledScript) -> CompiledModule { let mut script = compiled_script; // Add the "" identifier if it isn't present. diff --git a/language/tools/move-mv-llvm-compiler/Cargo.toml b/language/tools/move-mv-llvm-compiler/Cargo.toml index 5093284acc..8fecdc7a5d 100644 --- a/language/tools/move-mv-llvm-compiler/Cargo.toml +++ b/language/tools/move-mv-llvm-compiler/Cargo.toml @@ -22,11 +22,15 @@ move-ir-types = { path = "../../move-ir/types" } move-binary-format = { path = "../../move-binary-format" } move-coverage = { path = "../move-coverage" } move-compiler = { path = "../../move-compiler" } +move-model = { path = "../../move-model" } +move-stackless-bytecode = { path = "../../move-prover/bytecode" } clap = { version = "3.1.8", features = ["derive"] } #inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm14-0"] } semver = "1.0.13" llvm-sys = "150.0.3" +llvm-extra-sys = { path = "./llvm-extra-sys" } +extension-trait = "1.0.1" [dev-dependencies] datatest-stable = "0.1.1" diff --git a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/Cargo.toml b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/Cargo.toml new file mode 100644 index 0000000000..31f2a6906c --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "llvm-extra-sys" +version = "0.1.0" +license = "Apache 2.0 / MIT" +edition = "2021" + +[dependencies] +libc = "0.2" +llvm-sys = "150.0.3" + +[build-dependencies] +cc = "1.0.78" +anyhow = "1.0.68" diff --git a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/build.rs b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/build.rs new file mode 100644 index 0000000000..183ef397fe --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/build.rs @@ -0,0 +1,66 @@ +use anyhow::bail; +use std::path::PathBuf; +use std::process::Command; + +fn main() -> anyhow::Result<()> { + // Get the path to llvm-config from the llvm-sys crate + let llvm_config_path = std::env::var("DEP_LLVM_15_CONFIG_PATH")?; + let llvm_config_path = PathBuf::from(llvm_config_path); + let llvm_config = LlvmConfig::new(llvm_config_path); + let llvm_include_dir = llvm_config.include_dir()?; + let cxxflags = llvm_config.cxxflags()?; + let cxxflags = split_flags(&cxxflags); + + let mut cc = cc::Build::new(); + + for flag in cxxflags { + cc.flag(&flag); + } + + cc.cpp(true) + .warnings(false) + .include(llvm_include_dir) + .file("src/llvm-extra.cpp") + .compile("llvm-extra"); + + println!("cargo:rerun-if-changed=src/llvm-extra.cpp"); + + Ok(()) +} + +fn split_flags(flags: &str) -> Vec { + flags + .split_ascii_whitespace() + .map(|slice| slice.to_string()) + .collect() +} + +struct LlvmConfig { + path: PathBuf, +} + +impl LlvmConfig { + fn new(path: PathBuf) -> LlvmConfig { + LlvmConfig { path } + } + + fn include_dir(&self) -> anyhow::Result { + let out = Command::new(&self.path).arg("--includedir").output()?; + + if !out.status.success() { + bail!("llvm-config returned non-zero exit code"); + } + + Ok(PathBuf::from(String::from_utf8(out.stdout)?.trim())) + } + + fn cxxflags(&self) -> anyhow::Result { + let out = Command::new(&self.path).arg("--cxxflags").output()?; + + if !out.status.success() { + bail!("llvm-config returned non-zero exit code"); + } + + Ok(String::from_utf8(out.stdout)?) + } +} diff --git a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/lib.rs b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/lib.rs new file mode 100644 index 0000000000..9e499dd428 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/lib.rs @@ -0,0 +1,99 @@ +#![allow(non_snake_case)] + +use libc::{c_uint, size_t}; +use llvm_sys::prelude::*; + +pub fn AddFunctionAttributes<'ll>( + llfn: LLVMValueRef, + idx: AttributePlace, + attrs: &[LLVMAttributeRef], +) { + unsafe { + LLVMRustAddFunctionAttributes(llfn, idx.as_uint(), attrs.as_ptr(), attrs.len()); + } +} + +pub fn AddCallSiteAttributes<'ll>( + llfn: LLVMValueRef, + idx: AttributePlace, + attrs: &[LLVMAttributeRef], +) { + unsafe { + LLVMRustAddCallSiteAttributes(llfn, idx.as_uint(), attrs.as_ptr(), attrs.len()); + } +} + +#[derive(Copy, Clone)] +pub enum AttributePlace { + ReturnValue, + Argument(u32), + Function, +} + +impl AttributePlace { + pub fn as_uint(self) -> c_uint { + match self { + AttributePlace::ReturnValue => 0, + AttributePlace::Argument(i) => 1 + i, + AttributePlace::Function => !0, + } + } +} + +extern "C" { + pub fn LLVMRustCreateAttrNoValue(C: LLVMContextRef, attr: AttributeKind) -> LLVMAttributeRef; + + fn LLVMRustAddFunctionAttributes( + Fn: LLVMValueRef, + index: c_uint, + Attrs: *const LLVMAttributeRef, + AttrsLen: size_t, + ); + + fn LLVMRustAddCallSiteAttributes( + Instr: LLVMValueRef, + index: c_uint, + Attrs: *const LLVMAttributeRef, + AttrsLen: size_t, + ); +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub enum AttributeKind { + AlwaysInline = 0, + ByVal = 1, + Cold = 2, + InlineHint = 3, + MinSize = 4, + Naked = 5, + NoAlias = 6, + NoCapture = 7, + NoInline = 8, + NonNull = 9, + NoRedZone = 10, + NoReturn = 11, + NoUnwind = 12, + OptimizeForSize = 13, + ReadOnly = 14, + SExt = 15, + StructRet = 16, + UWTable = 17, + ZExt = 18, + InReg = 19, + SanitizeThread = 20, + SanitizeAddress = 21, + SanitizeMemory = 22, + NonLazyBind = 23, + OptimizeNone = 24, + ReturnsTwice = 25, + ReadNone = 26, + InaccessibleMemOnly = 27, + SanitizeHWAddress = 28, + WillReturn = 29, + StackProtectReq = 30, + StackProtectStrong = 31, + StackProtect = 32, + NoUndef = 33, + SanitizeMemTag = 34, +} diff --git a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/llvm-extra.cpp b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/llvm-extra.cpp new file mode 100644 index 0000000000..64e0f1e487 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/llvm-extra.cpp @@ -0,0 +1,166 @@ +// Most of this is copied from Rust + +#include "llvm-c/Core.h" + +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DiagnosticHandler.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Mangler.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Pass.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/Support/Signals.h" +#include "llvm/ADT/Optional.h" + +using namespace llvm; +using namespace llvm::sys; + +enum LLVMRustAttribute { + AlwaysInline = 0, + ByVal = 1, + Cold = 2, + InlineHint = 3, + MinSize = 4, + Naked = 5, + NoAlias = 6, + NoCapture = 7, + NoInline = 8, + NonNull = 9, + NoRedZone = 10, + NoReturn = 11, + NoUnwind = 12, + OptimizeForSize = 13, + ReadOnly = 14, + SExt = 15, + StructRet = 16, + UWTable = 17, + ZExt = 18, + InReg = 19, + SanitizeThread = 20, + SanitizeAddress = 21, + SanitizeMemory = 22, + NonLazyBind = 23, + OptimizeNone = 24, + ReturnsTwice = 25, + ReadNone = 26, + InaccessibleMemOnly = 27, + SanitizeHWAddress = 28, + WillReturn = 29, + StackProtectReq = 30, + StackProtectStrong = 31, + StackProtect = 32, + NoUndef = 33, + SanitizeMemTag = 34, +}; + +static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { + switch (Kind) { + case AlwaysInline: + return Attribute::AlwaysInline; + case ByVal: + return Attribute::ByVal; + case Cold: + return Attribute::Cold; + case InlineHint: + return Attribute::InlineHint; + case MinSize: + return Attribute::MinSize; + case Naked: + return Attribute::Naked; + case NoAlias: + return Attribute::NoAlias; + case NoCapture: + return Attribute::NoCapture; + case NoInline: + return Attribute::NoInline; + case NonNull: + return Attribute::NonNull; + case NoRedZone: + return Attribute::NoRedZone; + case NoReturn: + return Attribute::NoReturn; + case NoUnwind: + return Attribute::NoUnwind; + case OptimizeForSize: + return Attribute::OptimizeForSize; + case ReadOnly: + return Attribute::ReadOnly; + case SExt: + return Attribute::SExt; + case StructRet: + return Attribute::StructRet; + case UWTable: + return Attribute::UWTable; + case ZExt: + return Attribute::ZExt; + case InReg: + return Attribute::InReg; + case SanitizeThread: + return Attribute::SanitizeThread; + case SanitizeAddress: + return Attribute::SanitizeAddress; + case SanitizeMemory: + return Attribute::SanitizeMemory; + case NonLazyBind: + return Attribute::NonLazyBind; + case OptimizeNone: + return Attribute::OptimizeNone; + case ReturnsTwice: + return Attribute::ReturnsTwice; + case ReadNone: + return Attribute::ReadNone; + case InaccessibleMemOnly: + return Attribute::InaccessibleMemOnly; + case SanitizeHWAddress: + return Attribute::SanitizeHWAddress; + case WillReturn: + return Attribute::WillReturn; + case StackProtectReq: + return Attribute::StackProtectReq; + case StackProtectStrong: + return Attribute::StackProtectStrong; + case StackProtect: + return Attribute::StackProtect; + case NoUndef: + return Attribute::NoUndef; + case SanitizeMemTag: + return Attribute::SanitizeMemTag; + } + report_fatal_error("bad AttributeKind"); +} + +template static inline void AddAttributes(T *t, unsigned Index, + LLVMAttributeRef *Attrs, size_t AttrsLen) { + AttributeList PAL = t->getAttributes(); + AttributeList PALNew; + AttrBuilder B(t->getContext()); + for (LLVMAttributeRef Attr : makeArrayRef(Attrs, AttrsLen)) + B.addAttribute(unwrap(Attr)); + PALNew = PAL.addAttributesAtIndex(t->getContext(), Index, B); + t->setAttributes(PALNew); +} + +extern "C" void LLVMRustAddFunctionAttributes(LLVMValueRef Fn, unsigned Index, + LLVMAttributeRef *Attrs, size_t AttrsLen) { + Function *F = unwrap(Fn); + AddAttributes(F, Index, Attrs, AttrsLen); +} + +extern "C" void LLVMRustAddCallSiteAttributes(LLVMValueRef Instr, unsigned Index, + LLVMAttributeRef *Attrs, size_t AttrsLen) { + CallBase *Call = unwrap(Instr); + AddAttributes(Call, Index, Attrs, AttrsLen); +} + +extern "C" LLVMAttributeRef LLVMRustCreateAttrNoValue(LLVMContextRef C, + LLVMRustAttribute RustAttr) { + return wrap(Attribute::get(*unwrap(C), fromRust(RustAttr))); +} diff --git a/language/tools/move-mv-llvm-compiler/src/cstr.rs b/language/tools/move-mv-llvm-compiler/src/cstr.rs new file mode 100644 index 0000000000..5f5a01c87b --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/src/cstr.rs @@ -0,0 +1,40 @@ +//! A simple foolproof C-string cache. + +use std::cell::RefCell; +use std::collections::HashMap; +use std::ffi::CString; +use std::thread_local; + +pub trait SafeCStr { + fn cstr(&self) -> *const libc::c_char; +} + +impl SafeCStr for T +where + T: AsRef, +{ + fn cstr(&self) -> *const libc::c_char { + cstr(self.as_ref()) + } +} + +fn cstr(s: &str) -> *const libc::c_char { + thread_local! { + static MAP: RefCell> = RefCell::new(HashMap::new()); + } + + MAP.with(|map| { + let mut map = map.borrow_mut(); + if let Some(val) = map.get(s) { + val.as_ptr() + } else { + // Allocate space for null byte, applied by CString::new + let mut new_str = String::with_capacity(s.len() + 1); + new_str.push_str(s); + let new_cstr = CString::new(new_str).expect("null byte"); + let ptr = new_cstr.as_ptr(); + map.insert(s.to_owned(), new_cstr); + ptr + } + }) +} diff --git a/language/tools/move-mv-llvm-compiler/src/lib.rs b/language/tools/move-mv-llvm-compiler/src/lib.rs index 7e477f21a7..875467581f 100644 --- a/language/tools/move-mv-llvm-compiler/src/lib.rs +++ b/language/tools/move-mv-llvm-compiler/src/lib.rs @@ -5,4 +5,6 @@ pub mod disassembler; pub mod errors; pub mod move_bpf_module; -pub mod support; \ No newline at end of file +pub mod support; +pub mod stackless; +pub mod cstr; diff --git a/language/tools/move-mv-llvm-compiler/src/main.rs b/language/tools/move-mv-llvm-compiler/src/main.rs index aaf7622131..767145fe68 100644 --- a/language/tools/move-mv-llvm-compiler/src/main.rs +++ b/language/tools/move-mv-llvm-compiler/src/main.rs @@ -1,3 +1,5 @@ +#![allow(unused)] + // Copyright (c) The Diem Core Contributors // Copyright (c) The Move Contributors // SPDX-License-Identifier: Apache-2.0 @@ -18,6 +20,7 @@ use move_mv_llvm_compiler::disassembler::{Disassembler}; use move_ir_types::location::Spanned; use std::{fs, path::Path}; use llvm_sys::core::LLVMContextCreate; +use llvm_sys::prelude::LLVMModuleRef; #[derive(Debug, Parser)] #[clap(author, version, about)] @@ -107,11 +110,94 @@ fn main() -> anyhow::Result<()> { source_mapping.with_source_code((source_path.to_str().unwrap().to_string(), source_code)); } + let model_env = { + // fixme redundant bytecode deserialization + let move_module = if args.is_script { + let script = CompiledScript::deserialize(&bytecode_bytes) + .context("Script blob can't be deserialized")?; + move_model::script_into_module(script) + } else { + CompiledModule::deserialize(&bytecode_bytes) + .context("Module blob can't be deserialized")? + }; + + move_model::run_bytecode_model_builder( + [&move_module] + )? + }; + + /* let llvm_context = unsafe { LLVMContextCreate() }; - let mut disassembler = Disassembler::new(source_mapping, llvm_context); + + let move_module = model_env.get_modules().next().expect("module"); + let mut disassembler = Disassembler::new(source_mapping, move_module, llvm_context); let module = disassembler.disassemble() .context("Failed to disassemble bytecode")?; disassembler.llvm_write_to_file(module, args.llvm_ir, &args.output_file_path)?; + */ + + { + use move_mv_llvm_compiler::stackless::*; + + let mod_id = model_env.get_modules() + .take(1).map(|m| m.get_id()).next().expect("."); + let global_cx = GlobalContext::new(&model_env, Target::Solana); + let mod_cx = global_cx.create_module_context(mod_id); + let mut llmod = mod_cx.translate(); + llvm_write_to_file(llmod.as_mut(), args.llvm_ir, &args.output_file_path)?; + }; + + Ok(()) +} + +pub fn llvm_write_to_file(module: LLVMModuleRef, llvm_ir: bool, output_file_name: &String) -> anyhow::Result<()> { + use llvm_sys::bit_writer::LLVMWriteBitcodeToFD; + use llvm_sys::core::{LLVMPrintModuleToFile, LLVMPrintModuleToString, LLVMDisposeMessage}; + use std::os::unix::io::AsRawFd; + use std::fs::File; + use std::ptr; + use std::ffi::CStr; + use move_mv_llvm_compiler::support::to_c_str; + + unsafe { + if llvm_ir { + if output_file_name != "-" { + let mut err_string = ptr::null_mut(); + let res = LLVMPrintModuleToFile(module, + to_c_str(&output_file_name).as_ptr(), + &mut err_string, + ); + + if res != 0 { + assert!(!err_string.is_null()); + let msg = CStr::from_ptr(err_string).to_string_lossy(); + LLVMDisposeMessage(err_string); + anyhow::bail!("{}", msg); + } + } else { + let buf = LLVMPrintModuleToString(module); + assert!(!buf.is_null()); + let cstr = CStr::from_ptr(buf); + print!("{}", cstr.to_string_lossy()); + LLVMDisposeMessage(buf); + } + } else { + if output_file_name == "-" { + anyhow::bail!("Not writing bitcode to stdout"); + } + let bc_file = File::create(&output_file_name)?; + let res = LLVMWriteBitcodeToFD( + module, + bc_file.as_raw_fd(), + false as i32, + true as i32, + ); + + if res != 0 { + anyhow::bail!("Failed to write bitcode to file"); + } + } + } Ok(()) } diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs b/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs new file mode 100644 index 0000000000..741c5a4aec --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs @@ -0,0 +1,359 @@ +//! LLVM wrappers. +//! +//! The stackless code generator accesses llvm only through this mod. +//! +//! It: +//! +//! - Runs dtors +//! - Encapsulates unsafety, though making LLVM fully memsafe is hard. +//! - Hides weirdly mutable array pointers. +//! - Provides high-level instruction builders compatible with the stackless bytecode model. + +use llvm_extra_sys::*; +use llvm_sys::core::*; +use llvm_sys::prelude::*; + +use crate::cstr::SafeCStr; + +use std::ptr; + +pub use llvm_extra_sys::AttributeKind; +pub use llvm_sys::LLVMIntPredicate; + +pub struct Context(LLVMContextRef); + +impl Drop for Context { + fn drop(&mut self) { + unsafe { + LLVMContextDispose(self.0); + } + } +} + +impl Context { + pub fn new() -> Context { + unsafe { Context(LLVMContextCreate()) } + } + + pub fn create_module(&self, name: &str) -> Module { + unsafe { Module(LLVMModuleCreateWithNameInContext(name.cstr(), self.0)) } + } + + pub fn create_builder(&self) -> Builder { + unsafe { Builder(LLVMCreateBuilderInContext(self.0)) } + } + + pub fn void_type(&self) -> Type { + unsafe { Type(LLVMVoidTypeInContext(self.0)) } + } + + pub fn int1_type(&self) -> Type { + unsafe { Type(LLVMInt1TypeInContext(self.0)) } + } + + pub fn int8_type(&self) -> Type { + unsafe { Type(LLVMInt8TypeInContext(self.0)) } + } + + pub fn int64_type(&self) -> Type { + unsafe { Type(LLVMInt64TypeInContext(self.0)) } + } +} + +pub struct Module(LLVMModuleRef); + +impl Drop for Module { + fn drop(&mut self) { + unsafe { + LLVMDisposeModule(self.0); + } + } +} + +impl AsMut for Module { + fn as_mut(&mut self) -> &mut llvm_sys::LLVMModule { + unsafe { &mut *self.0 } + } +} + +impl Module { + pub fn set_target(&self, triple: &str) { + unsafe { + LLVMSetTarget(self.0, triple.cstr()); + } + } + + pub fn set_source_file_name(&self, name: &str) { + unsafe { LLVMSetSourceFileName(self.0, name.as_ptr() as *const libc::c_char, name.len()) } + } + + pub fn add_function(&self, name: &str, ty: FunctionType) -> Function { + unsafe { Function(LLVMAddFunction(self.0, name.cstr(), ty.0)) } + } + + pub fn add_function_with_attrs( + &self, + name: &str, + ty: FunctionType, + attrs: &[AttributeKind], + ) -> Function { + unsafe { + let cx = LLVMGetModuleContext(self.0); + let attrs = attrs + .iter() + .map(|a| LLVMRustCreateAttrNoValue(cx, *a)) + .collect::>(); + + let llfn = self.add_function(name, ty); + + AddFunctionAttributes(llfn.0, AttributePlace::Function, &attrs); + + llfn + } + } + + pub fn get_named_function(&self, name: &str) -> Option { + unsafe { + let llfn = LLVMGetNamedFunction(self.0, name.cstr()); + if !llfn.is_null() { + Some(Function(llfn)) + } else { + None + } + } + } + + pub fn verify(&self) { + use llvm_sys::analysis::*; + unsafe { + LLVMVerifyModule( + self.0, + LLVMVerifierFailureAction::LLVMAbortProcessAction, + ptr::null_mut(), + ); + } + } +} + +pub struct Builder(LLVMBuilderRef); + +impl Drop for Builder { + fn drop(&mut self) { + unsafe { + LLVMDisposeBuilder(self.0); + } + } +} + +impl Builder { + pub fn position_at_end(&self, bb: BasicBlock) { + unsafe { + LLVMPositionBuilderAtEnd(self.0, bb.0); + } + } + + pub fn build_alloca(&self, ty: Type, name: Name) -> Alloca { + unsafe { Alloca(LLVMBuildAlloca(self.0, ty.0, name.0.cstr())) } + } + + pub fn store_param_to_alloca(&self, param: Parameter, alloca: Alloca) { + unsafe { + LLVMBuildStore(self.0, param.0, alloca.0); + } + } + + pub fn load_store(&self, ty: Type, src: Alloca, dst: Alloca) { + unsafe { + let tmp_name = Name::kind("load_store_tmp"); + let tmp_reg = LLVMBuildLoad2(self.0, ty.0, src.0, tmp_name.0.cstr()); + LLVMBuildStore(self.0, tmp_reg, dst.0); + } + } + + pub fn build_return_void(&self) { + unsafe { + LLVMBuildRetVoid(self.0); + } + } + + pub fn load_return(&self, ty: Type, val: Alloca) { + unsafe { + let tmp_name = Name::kind("retval"); + let tmp_reg = LLVMBuildLoad2(self.0, ty.0, val.0, tmp_name.0.cstr()); + LLVMBuildRet(self.0, tmp_reg); + } + } + + pub fn store_const(&self, src: Constant, dst: Alloca) { + unsafe { + LLVMBuildStore(self.0, src.0, dst.0); + } + } + + pub fn build_br(&self, bb: BasicBlock) { + unsafe { + LLVMBuildBr(self.0, bb.0); + } + } + + pub fn load_cond_br(&self, ty: Type, val: Alloca, bb0: BasicBlock, bb1: BasicBlock) { + unsafe { + let cnd_name = Name::kind("cnd"); + let cnd_reg = LLVMBuildLoad2(self.0, ty.0, val.0, cnd_name.0.cstr()); + LLVMBuildCondBr(self.0, cnd_reg, bb0.0, bb1.0); + } + } + + pub fn load_call(&self, fnty: FunctionType, fnval: Function, args: &[(Type, Alloca)]) { + unsafe { + let mut args = args + .iter() + .enumerate() + .map(|(i, (ty, val))| { + let name = Name::new("call_arg", i); + LLVMBuildLoad2(self.0, ty.0, val.0, name.0.cstr()) + }) + .collect::>(); + LLVMBuildCall2( + self.0, + fnty.0, + fnval.0, + args.as_mut_ptr(), + args.len() as libc::c_uint, + "".cstr(), + ); + } + } + + pub fn build_unreachable(&self) { + unsafe { + LLVMBuildUnreachable(self.0); + } + } + + pub fn load_add_store(&self, ty: Type, src0: Alloca, src1: Alloca, dst: Alloca) { + unsafe { + let src0_name = Name::new("add_src", 0); + let src1_name = Name::new("add_src", 1); + let dst_name = Name::kind("add_dst"); + let src0_reg = LLVMBuildLoad2(self.0, ty.0, src0.0, src0_name.0.cstr()); + let src1_reg = LLVMBuildLoad2(self.0, ty.0, src1.0, src1_name.0.cstr()); + let dst_reg = LLVMBuildAdd(self.0, src0_reg, src1_reg, dst_name.0.cstr()); + LLVMBuildStore(self.0, dst_reg, dst.0); + } + } + + pub fn load_icmp_store( + &self, + ty: Type, + src0: Alloca, + src1: Alloca, + dst: Alloca, + pred: LLVMIntPredicate, + ) { + unsafe { + let src0_name = Name::new("icmp_src", 0); + let src1_name = Name::new("icmp_src", 1); + let dst_name = Name::kind("icmp_dst"); + let src0_reg = LLVMBuildLoad2(self.0, ty.0, src0.0, src0_name.0.cstr()); + let src1_reg = LLVMBuildLoad2(self.0, ty.0, src1.0, src1_name.0.cstr()); + let dst_reg = LLVMBuildICmp(self.0, pred, src0_reg, src1_reg, dst_name.0.cstr()); + LLVMBuildStore(self.0, dst_reg, dst.0); + } + } +} + +#[derive(Copy, Clone)] +pub struct Type(LLVMTypeRef); + +impl Type {} + +#[derive(Copy, Clone)] +pub struct FunctionType(LLVMTypeRef); + +impl FunctionType { + pub fn new(return_type: Type, parameter_types: &[Type]) -> FunctionType { + let mut parameter_types: Vec<_> = parameter_types.iter().map(|t| t.0).collect(); + unsafe { + FunctionType(LLVMFunctionType( + return_type.0, + parameter_types.as_mut_ptr(), + parameter_types.len() as libc::c_uint, + false as LLVMBool, + )) + } + } +} + +pub struct Function(LLVMValueRef); + +impl Function { + pub fn append_basic_block(&self, name: N) -> BasicBlock + where + N: Into, + { + unsafe { BasicBlock(LLVMAppendBasicBlock(self.0, name.into().0.cstr())) } + } + + pub fn get_param(&self, i: usize) -> Parameter { + unsafe { Parameter(LLVMGetParam(self.0, i as u32)) } + } + + pub fn llvm_type(&self) -> FunctionType { + unsafe { FunctionType(LLVMTypeOf(self.0)) } + } + + pub fn verify(&self) { + use llvm_sys::analysis::*; + unsafe { + LLVMVerifyFunction(self.0, LLVMVerifierFailureAction::LLVMAbortProcessAction); + } + } +} + +#[derive(Copy, Clone)] +pub struct BasicBlock(LLVMBasicBlockRef); + +impl BasicBlock {} + +/// A local identifier, variable, block, etc. +/// +/// The trailing underscore is to visually distinguish cases where llvm renames +/// them with an appended number. +pub struct Name(String); + +impl Name { + pub fn new(kind: &str, ident: S) -> Name + where + S: std::fmt::Display, + { + Name(format!("{kind}_{ident}_")) + } + + pub fn kind(kind: &str) -> Name { + Name(format!("{kind}_")) + } +} + +impl<'a> Into for &'a str { + fn into(self) -> Name { + Name::kind(self) + } +} + +#[derive(Copy, Clone)] +pub struct Alloca(LLVMValueRef); + +impl Alloca {} + +pub struct Parameter(LLVMValueRef); + +impl Parameter {} + +pub struct Constant(LLVMValueRef); + +impl Constant { + pub fn int(ty: Type, v: u64) -> Constant { + unsafe { Constant(LLVMConstInt(ty.0, v, false as LLVMBool)) } + } +} diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs b/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs new file mode 100644 index 0000000000..fa27d08940 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs @@ -0,0 +1,434 @@ +use self::exts::*; +use move_model::ast as mast; +use move_model::model as mm; +use move_model::ty as mty; +use move_stackless_bytecode::stackless_bytecode as sbc; +use std::collections::HashMap; + +mod llvm; + +#[derive(Copy, Clone)] +pub enum Target { + Solana, +} + +impl Target { + pub fn triple(&self) -> &'static str { + match self { + Target::Solana => "bpfel-unknown-unknown", + } + } +} + +pub struct GlobalContext<'a> { + env: &'a mm::GlobalEnv, + llvm_cx: llvm::Context, + target: Target, +} + +impl<'a> GlobalContext<'a> { + pub fn new(env: &mm::GlobalEnv, target: Target) -> GlobalContext { + GlobalContext { + env, + llvm_cx: llvm::Context::new(), + target, + } + } + + pub fn create_module_context(&self, id: mm::ModuleId) -> ModuleContext { + let env = self.env.get_module(id); + let name = env.llvm_module_name(); + ModuleContext { + env, + llvm_cx: &self.llvm_cx, + llvm_module: self.llvm_cx.create_module(&name), + llvm_builder: self.llvm_cx.create_builder(), + target: self.target, + } + } +} + +pub struct ModuleContext<'a> { + env: mm::ModuleEnv<'a>, + llvm_cx: &'a llvm::Context, + llvm_module: llvm::Module, + llvm_builder: llvm::Builder, + target: Target, +} + +impl<'a> ModuleContext<'a> { + pub fn translate(self) -> llvm::Module { + self.llvm_module.set_target(self.target.triple()); + + let filename = self.env.get_source_path().to_str().expect("utf-8"); + self.llvm_module.set_source_file_name(filename); + + for fn_env in self.env.get_functions() { + let fn_cx = self.create_fn_context(fn_env); + fn_cx.translate(); + } + + self.llvm_module.verify(); + + self.llvm_module + } + + fn create_fn_context<'b>(&'b self, fn_env: mm::FunctionEnv<'b>) -> FunctionContext<'b> { + let locals = Vec::with_capacity(fn_env.get_local_count()); + FunctionContext { + env: fn_env, + llvm_cx: &self.llvm_cx, + llvm_module: &self.llvm_module, + llvm_builder: &self.llvm_builder, + label_blocks: HashMap::new(), + locals, + } + } +} + +struct FunctionContext<'a> { + env: mm::FunctionEnv<'a>, + llvm_cx: &'a llvm::Context, + llvm_module: &'a llvm::Module, + llvm_builder: &'a llvm::Builder, + label_blocks: HashMap, + /// Corresponds to FunctionData:local_types + locals: Vec, +} + +/// A stackless move local variable, translated as an llvm alloca +struct Local { + mty: mty::Type, + llty: llvm::Type, + llval: llvm::Alloca, +} + +impl<'a> FunctionContext<'a> { + fn translate(mut self) { + use move_stackless_bytecode::stackless_bytecode_generator::StacklessBytecodeGenerator; + + let fn_data = StacklessBytecodeGenerator::new(&self.env).generate_function(); + + dbg!(&fn_data); + + // Create the llvm function + let ll_fn = { + let ll_fnty = { + let ll_rty = match fn_data.return_types.len() { + 0 => self.llvm_cx.void_type(), + 1 => self.llvm_type(&fn_data.return_types[0]), + _ => { + todo!() + } + }; + + let ll_parm_tys = self + .env + .get_parameter_types() + .iter() + .map(|mty| self.llvm_type(mty)) + .collect::>(); + + llvm::FunctionType::new(ll_rty, &ll_parm_tys) + }; + + self.llvm_module + .add_function(&self.env.llvm_symbol_name(), ll_fnty) + }; + + // Create basic blocks and position builder at entry block + { + let entry_block = ll_fn.append_basic_block("entry"); + + // Create basic blocks for move labels + for instr in &fn_data.code { + match instr { + sbc::Bytecode::Label(_, label) => { + let name = llvm::Name::new("bb", label.as_usize()); + let llbb = ll_fn.append_basic_block(name); + self.label_blocks.insert(*label, llbb); + } + _ => {} + } + } + + self.llvm_builder.position_at_end(entry_block); + } + + // Declare all the locals as allocas + { + for (i, mty) in fn_data.local_types.iter().enumerate() { + let llty = self.llvm_type(mty); + let name = llvm::Name::new("local", i); + let llval = self.llvm_builder.build_alloca(llty, name); + self.locals.push(Local { + mty: mty.clone(), // fixme bad clone + llty, + llval, + }); + } + } + + // Store params into locals + { + let param_count = self.env.get_parameter_count(); + let ll_params = (0..param_count).map(|i| ll_fn.get_param(i)); + + for (ll_param, local) in ll_params.zip(self.locals.iter()) { + self.llvm_builder + .store_param_to_alloca(ll_param, local.llval); + } + } + + // Translate instructions + for instr in &fn_data.code { + self.translate_instruction(instr); + } + + ll_fn.verify(); + } + + fn translate_instruction(&self, instr: &sbc::Bytecode) { + match instr { + sbc::Bytecode::Assign(_, dst, src, sbc::AssignKind::Move) => { + let mty = &self.locals[*dst].mty; + let llty = self.locals[*dst].llty; + let dst_llval = self.locals[*dst].llval; + let src_llval = self.locals[*src].llval; + match mty { + mty::Type::Primitive(mty::PrimitiveType::Bool | mty::PrimitiveType::U8) => { + self.llvm_builder.load_store(llty, src_llval, dst_llval); + } + _ => todo!(), + } + } + sbc::Bytecode::Assign(_, dst, src, sbc::AssignKind::Store) => { + let mty = &self.locals[*dst].mty; + let llty = self.locals[*dst].llty; + let dst_llval = self.locals[*dst].llval; + let src_llval = self.locals[*src].llval; + match mty { + mty::Type::Primitive(mty::PrimitiveType::Bool | mty::PrimitiveType::U8) => { + self.llvm_builder.load_store(llty, src_llval, dst_llval); + } + _ => todo!(), + } + } + sbc::Bytecode::Call(_, dst, op, src, None) => { + self.translate_call(dst, op, src); + } + sbc::Bytecode::Ret(_, vals) => match vals.len() { + 0 => { + self.llvm_builder.build_return_void(); + } + 1 => { + let idx = vals[0]; + let llval = self.locals[idx].llval; + let llty = self.locals[idx].llty; + self.llvm_builder.load_return(llty, llval); + } + _ => todo!(), + }, + sbc::Bytecode::Load(_, idx, val) => { + let local_llval = self.locals[*idx].llval; + let const_llval = self.constant(val); + self.llvm_builder.store_const(const_llval, local_llval); + } + sbc::Bytecode::Branch(_, label0, label1, cnd_idx) => { + let cnd_llval = self.locals[*cnd_idx].llval; + let cnd_llty = self.locals[*cnd_idx].llty; + let bb0 = self.label_blocks[label0]; + let bb1 = self.label_blocks[label1]; + self.llvm_builder + .load_cond_br(cnd_llty, cnd_llval, bb0, bb1); + } + sbc::Bytecode::Jump(_, label) => { + let llbb = self.label_blocks[label]; + self.llvm_builder.build_br(llbb); + } + sbc::Bytecode::Label(_, label) => { + let llbb = self.label_blocks[label]; + self.llvm_builder.position_at_end(llbb); + } + sbc::Bytecode::Abort(_, local) => { + self.emit_rtcall(RtCall::Abort(*local)); + } + _ => { + todo!() + } + } + } + + fn translate_call( + &self, + dst: &[mast::TempIndex], + op: &sbc::Operation, + src: &[mast::TempIndex], + ) { + use sbc::Operation; + match op { + Operation::Destroy => { + assert!(dst.is_empty()); + assert_eq!(src.len(), 1); + let idx = src[0]; + let mty = &self.locals[idx].mty; + match mty { + mty::Type::Primitive(_) => ( /* nop */ ), + _ => todo!(), + } + } + Operation::Add => { + assert_eq!(dst.len(), 1); + assert_eq!(src.len(), 2); + let dst_idx = dst[0]; + let src0_idx = src[0]; + let src1_idx = src[1]; + let dst_llval = self.locals[dst_idx].llval; + let src0_llval = self.locals[src0_idx].llval; + let src1_llval = self.locals[src1_idx].llval; + let mty = &self.locals[src0_idx].mty; + let llty = self.locals[src0_idx].llty; + match mty { + mty::Type::Primitive(mty::PrimitiveType::U8) => { + self.llvm_builder + .load_add_store(llty, src0_llval, src1_llval, dst_llval); + } + _ => todo!(), + } + } + Operation::Sub => todo!(), + Operation::Mul => todo!(), + Operation::Div => todo!(), + Operation::Mod => todo!(), + Operation::BitOr => todo!(), + Operation::BitAnd => todo!(), + Operation::Xor => todo!(), + Operation::Shl => todo!(), + Operation::Shr => todo!(), + Operation::Eq => { + assert_eq!(dst.len(), 1); + assert_eq!(src.len(), 2); + let dst_idx = dst[0]; + let src0_idx = src[0]; + let src1_idx = src[1]; + let dst_llval = self.locals[dst_idx].llval; + let src0_llval = self.locals[src0_idx].llval; + let src1_llval = self.locals[src1_idx].llval; + let mty = &self.locals[src0_idx].mty; + let llty = self.locals[src0_idx].llty; + match mty { + mty::Type::Primitive(mty::PrimitiveType::U8) => { + self.llvm_builder.load_icmp_store( + llty, + src0_llval, + src1_llval, + dst_llval, + llvm::LLVMIntPredicate::LLVMIntEQ, + ); + } + _ => todo!(), + } + } + _ => todo!(), + } + } + + fn llvm_type(&self, mty: &mty::Type) -> llvm::Type { + use mty::{PrimitiveType, Type}; + + match mty { + Type::Primitive(PrimitiveType::Bool) => self.llvm_cx.int1_type(), + Type::Primitive(PrimitiveType::U8) => self.llvm_cx.int8_type(), + Type::Primitive(PrimitiveType::U64) => self.llvm_cx.int64_type(), + _ => { + todo!() + } + } + } + + fn constant(&self, mc: &sbc::Constant) -> llvm::Constant { + use sbc::Constant; + match mc { + Constant::U8(val) => { + let llty = self.llvm_cx.int8_type(); + llvm::Constant::int(llty, *val as u64) + } + Constant::U64(val) => { + let llty = self.llvm_cx.int64_type(); + llvm::Constant::int(llty, *val) + } + _ => todo!(), + } + } + + fn emit_rtcall(&self, rtcall: RtCall) { + match &rtcall { + RtCall::Abort(local_idx) => { + let (llfn, llfnty) = self.get_runtime_function(&rtcall); + let local_llval = self.locals[*local_idx].llval; + let local_llty = self.locals[*local_idx].llty; + self.llvm_builder + .load_call(llfnty, llfn, &[(local_llty, local_llval)]); + self.llvm_builder.build_unreachable(); + } + } + } + + fn get_runtime_function(&self, rtcall: &RtCall) -> (llvm::Function, llvm::FunctionType) { + let name = match rtcall { + RtCall::Abort(..) => "abort", + }; + let name = format!("move_rt_{name}"); + let llfn = self.llvm_module.get_named_function(&name); + if let Some(llfn) = llfn { + let llty = llfn.llvm_type(); + (llfn, llty) + } else { + let (llty, attrs) = match rtcall { + RtCall::Abort(..) => { + let ret_ty = self.llvm_cx.void_type(); + let param_tys = &[self.llvm_cx.int64_type()]; + let llty = llvm::FunctionType::new(ret_ty, param_tys); + let attrs = vec![llvm::AttributeKind::NoReturn]; + (llty, attrs) + } + }; + + let llfn = self + .llvm_module + .add_function_with_attrs(&name, llty, &attrs); + (llfn, llty) + } + } +} + +mod exts { + use super::mm; + use extension_trait::extension_trait; + + #[extension_trait] + pub impl<'a> ModuleEnvExt for mm::ModuleEnv<'a> { + fn llvm_module_name(&self) -> String { + self.get_full_name_str().replace(':', "_") + } + } + + #[extension_trait] + pub impl<'a> FunctionEnxExt for mm::FunctionEnv<'a> { + fn llvm_symbol_name(&self) -> String { + // fixme use get_full_name_str + let name = self.get_name_str(); + if name == "" { + // fixme move-model names script fns "". + // we might want to preserve the actual names + "main".to_string() + } else { + name + } + } + } +} + +pub enum RtCall { + Abort(mast::TempIndex), +} diff --git a/language/tools/move-mv-llvm-compiler/src/support/mod.rs b/language/tools/move-mv-llvm-compiler/src/support/mod.rs index 9c29bf3d85..742fb0e0b1 100644 --- a/language/tools/move-mv-llvm-compiler/src/support/mod.rs +++ b/language/tools/move-mv-llvm-compiler/src/support/mod.rs @@ -147,7 +147,7 @@ pub fn enable_llvm_pretty_stack_trace() { /// A) Finds a terminating null byte in the Rust string and can reference it directly like a C string. /// /// B) Finds no null byte and allocates a new C string based on the input Rust string. -pub(crate) fn to_c_str<'s>(mut s: &'s str) -> Cow<'s, CStr> { +pub fn to_c_str<'s>(mut s: &'s str) -> Cow<'s, CStr> { if s.is_empty() { s = "\0"; } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll new file mode 100644 index 0000000000..61452b9ba5 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll @@ -0,0 +1,17 @@ +; ModuleID = '0x100__Test' +source_filename = "" +target triple = "bpfel-unknown-unknown" + +define void @test() { +entry_: + %local_0_ = alloca i64, align 8 + store i64 10, ptr %local_0_, align 4 + %call_arg_0_ = load i64, ptr %local_0_, align 4 + call void @move_rt_abort(i64 %call_arg_0_) + unreachable +} + +; Function Attrs: noreturn +declare void @move_rt_abort(i64) #0 + +attributes #0 = { noreturn } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort.move b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort.move new file mode 100644 index 0000000000..7a38eea736 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort.move @@ -0,0 +1,5 @@ +module 0x100::Test { + fun test() { + abort 10 + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll new file mode 100644 index 0000000000..61452b9ba5 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll @@ -0,0 +1,17 @@ +; ModuleID = '0x100__Test' +source_filename = "" +target triple = "bpfel-unknown-unknown" + +define void @test() { +entry_: + %local_0_ = alloca i64, align 8 + store i64 10, ptr %local_0_, align 4 + %call_arg_0_ = load i64, ptr %local_0_, align 4 + call void @move_rt_abort(i64 %call_arg_0_) + unreachable +} + +; Function Attrs: noreturn +declare void @move_rt_abort(i64) #0 + +attributes #0 = { noreturn } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert.move b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert.move new file mode 100644 index 0000000000..ccdbddce38 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert.move @@ -0,0 +1,5 @@ +module 0x100::Test { + fun test() { + assert!(1 == 2, 10); + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll new file mode 100644 index 0000000000..ca67807b3c --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll @@ -0,0 +1,10 @@ +; ModuleID = '' +source_filename = "" +target triple = "bpfel-unknown-unknown" + +define void @main() { +entry_: + %local_0_ = alloca i8, align 1 + store i8 7, ptr %local_0_, align 1 + ret void +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign.move b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign.move new file mode 100644 index 0000000000..c47c84df6c --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign.move @@ -0,0 +1,5 @@ +script { + fun main() { + let a = 7u8; + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/empty-module-build/modules/0_Empty.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/empty-module-build/modules/0_Empty.expected.ll index 17763e4370..90f1121dc8 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/empty-module-build/modules/0_Empty.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/empty-module-build/modules/0_Empty.expected.ll @@ -1,7 +1,3 @@ -; ModuleID = 'module 100.Empty' -source_filename = "100.Empty.bc" +; ModuleID = '0x100__Empty' +source_filename = "" target triple = "bpfel-unknown-unknown" - -!llvm.module.flags = !{!0} - -!0 = !{i32 2, !"Debug Info Version", i64 3} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll new file mode 100644 index 0000000000..fcfead0df1 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll @@ -0,0 +1,24 @@ +; ModuleID = '0x100__Test' +source_filename = "" +target triple = "bpfel-unknown-unknown" + +define i1 @test(i8 %0, i8 %1) { +entry_: + %local_0_ = alloca i8, align 1 + %local_1_ = alloca i8, align 1 + %local_2_ = alloca i8, align 1 + %local_3_ = alloca i8, align 1 + %local_4_ = alloca i1, align 1 + store i8 %0, ptr %local_0_, align 1 + store i8 %1, ptr %local_1_, align 1 + %load_store_tmp_ = load i8, ptr %local_0_, align 1 + store i8 %load_store_tmp_, ptr %local_2_, align 1 + %load_store_tmp_1 = load i8, ptr %local_1_, align 1 + store i8 %load_store_tmp_1, ptr %local_3_, align 1 + %icmp_src_0_ = load i8, ptr %local_2_, align 1 + %icmp_src_1_ = load i8, ptr %local_3_, align 1 + %icmp_dst_ = icmp eq i8 %icmp_src_0_, %icmp_src_1_ + store i1 %icmp_dst_, ptr %local_4_, align 1 + %retval_ = load i1, ptr %local_4_, align 1 + ret i1 %retval_ +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq.move b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq.move new file mode 100644 index 0000000000..ff01fa79c1 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq.move @@ -0,0 +1,5 @@ +module 0x100::Test { + fun test(a: u8, b: u8): bool { + a == b + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll new file mode 100644 index 0000000000..6657fea8b5 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll @@ -0,0 +1,36 @@ +; ModuleID = '0x100__Test' +source_filename = "" +target triple = "bpfel-unknown-unknown" + +define i8 @test(i1 %0) { +entry_: + %local_0_ = alloca i1, align 1 + %local_1_ = alloca i8, align 1 + %local_2_ = alloca i1, align 1 + %local_3_ = alloca i8, align 1 + %local_4_ = alloca i8, align 1 + %local_5_ = alloca i8, align 1 + store i1 %0, ptr %local_0_, align 1 + %load_store_tmp_ = load i1, ptr %local_0_, align 1 + store i1 %load_store_tmp_, ptr %local_2_, align 1 + %cnd_ = load i1, ptr %local_2_, align 1 + br i1 %cnd_, label %bb_1_, label %bb_0_ + +bb_1_: ; preds = %entry_ + store i8 2, ptr %local_3_, align 1 + %load_store_tmp_1 = load i8, ptr %local_3_, align 1 + store i8 %load_store_tmp_1, ptr %local_1_, align 1 + br label %bb_2_ + +bb_0_: ; preds = %entry_ + store i8 3, ptr %local_4_, align 1 + %load_store_tmp_2 = load i8, ptr %local_4_, align 1 + store i8 %load_store_tmp_2, ptr %local_1_, align 1 + br label %bb_2_ + +bb_2_: ; preds = %bb_0_, %bb_1_ + %load_store_tmp_3 = load i8, ptr %local_1_, align 1 + store i8 %load_store_tmp_3, ptr %local_5_, align 1 + %retval_ = load i8, ptr %local_5_, align 1 + ret i8 %retval_ +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if.move b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if.move new file mode 100644 index 0000000000..c53e3e509b --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if.move @@ -0,0 +1,5 @@ +module 0x100::Test { + fun test(a: bool): u8 { + if (a) 2 else 3 + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll new file mode 100644 index 0000000000..71b13eb6b5 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll @@ -0,0 +1,24 @@ +; ModuleID = '0x100__Test' +source_filename = "" +target triple = "bpfel-unknown-unknown" + +define i8 @test(i8 %0, i8 %1) { +entry_: + %local_0_ = alloca i8, align 1 + %local_1_ = alloca i8, align 1 + %local_2_ = alloca i8, align 1 + %local_3_ = alloca i8, align 1 + %local_4_ = alloca i8, align 1 + store i8 %0, ptr %local_0_, align 1 + store i8 %1, ptr %local_1_, align 1 + %load_store_tmp_ = load i8, ptr %local_0_, align 1 + store i8 %load_store_tmp_, ptr %local_2_, align 1 + %load_store_tmp_1 = load i8, ptr %local_1_, align 1 + store i8 %load_store_tmp_1, ptr %local_3_, align 1 + %add_src_0_ = load i8, ptr %local_2_, align 1 + %add_src_1_ = load i8, ptr %local_3_, align 1 + %add_dst_ = add i8 %add_src_0_, %add_src_1_ + store i8 %add_dst_, ptr %local_4_, align 1 + %retval_ = load i8, ptr %local_4_, align 1 + ret i8 %retval_ +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8.move b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8.move new file mode 100644 index 0000000000..574eef1f0a --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8.move @@ -0,0 +1,6 @@ +module 0x100::Test { + fun test(a: u8, b: u8): u8 { + let c = a + b; + c + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll new file mode 100644 index 0000000000..7b67e51e9b --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll @@ -0,0 +1,11 @@ +; ModuleID = '0x100__Test' +source_filename = "" +target triple = "bpfel-unknown-unknown" + +define i8 @test() { +entry_: + %local_0_ = alloca i8, align 1 + store i8 100, ptr %local_0_, align 1 + %retval_ = load i8, ptr %local_0_, align 1 + ret i8 %retval_ +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return.move b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return.move new file mode 100644 index 0000000000..e007096344 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return.move @@ -0,0 +1,5 @@ +module 0x100::Test { + fun test(): u8 { + 100 + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll index 0f12021ac5..0b766c1cf8 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll @@ -1,12 +1,8 @@ -; ModuleID = 'script' -source_filename = "script.bc" +; ModuleID = '' +source_filename = "" target triple = "bpfel-unknown-unknown" define void @main() { -entry: - ret i64 0 +entry_: + ret void } - -!llvm.module.flags = !{!0} - -!0 = !{i32 2, !"Debug Info Version", i64 3} From 7d01d22096a128af4df6a0de6a84aeab1be3e015 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 6 Jan 2023 17:25:03 -0700 Subject: [PATCH 2/5] Add rbpf test harness --- .gitignore | 2 + Cargo.lock | 151 ++++++++--- Cargo.toml | 3 + .../tools/move-mv-llvm-compiler/Cargo.toml | 11 + .../tools/move-mv-llvm-compiler/src/cstr.rs | 2 +- .../tools/move-mv-llvm-compiler/src/main.rs | 19 +- .../src/stackless/llvm.rs | 122 +++++++++ .../src/stackless/mod.rs | 53 +++- .../move-mv-llvm-compiler/tests/ir-tests.rs | 9 +- .../tests/move-ir-tests.rs | 234 +++--------------- .../move-mv-llvm-compiler/tests/rbpf-tests.rs | 184 ++++++++++++++ .../rbpf-tests/script-build/scripts/main.mv | Bin 0 -> 18 bytes .../tests/rbpf-tests/script.move | 3 + .../tests/test_common.rs | 192 ++++++++++++++ 14 files changed, 743 insertions(+), 242 deletions(-) create mode 100644 language/tools/move-mv-llvm-compiler/tests/rbpf-tests.rs create mode 100644 language/tools/move-mv-llvm-compiler/tests/rbpf-tests/script-build/scripts/main.mv create mode 100644 language/tools/move-mv-llvm-compiler/tests/rbpf-tests/script.move create mode 100644 language/tools/move-mv-llvm-compiler/tests/test_common.rs diff --git a/.gitignore b/.gitignore index f70a2931c7..d59c46164e 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,5 @@ target-out-docker **/move-mv-llvm-compiler/**/ir-tests/*.mv **/move-mv-llvm-compiler/**/move-ir-tests/**/*.mv **/move-mv-llvm-compiler/**/move-ir-tests/**/*.actual.ll +**/move-mv-llvm-compiler/**/rbpf-tests/**/*.o +**/move-mv-llvm-compiler/**/rbpf-tests/**/*.so diff --git a/Cargo.lock b/Cargo.lock index 4069fcbbc0..51d521bd92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,6 +143,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -839,6 +845,19 @@ dependencies = [ "itertools 0.10.1", ] +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + [[package]] name = "combine" version = "4.6.4" @@ -1339,7 +1358,7 @@ dependencies = [ "once_cell", "proptest", "proptest-derive", - "rand 0.8.4", + "rand 0.8.5", "rand_core 0.6.2", "ripemd160", "serde 1.0.145", @@ -1535,7 +1554,7 @@ checksum = "97c6ac152eba578c1c53d2cefe8ad02e239e3d6f971b0f1ef3cb54cd66037fa0" dependencies = [ "curve25519-dalek-fiat", "ed25519", - "rand 0.8.4", + "rand 0.8.5", "serde 1.0.145", "serde_bytes", "sha2", @@ -1842,7 +1861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" dependencies = [ "byteorder", - "rand 0.8.4", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -2110,6 +2129,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "goblin" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" +dependencies = [ + "log", + "plain", + "scroll", +] + [[package]] name = "guppy" version = "0.12.6" @@ -2198,6 +2228,15 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.9.1" @@ -2933,7 +2972,7 @@ dependencies = [ "move-ir-to-bytecode", "move-ir-types", "move-symbol-pool", - "rand 0.8.4", + "rand 0.8.5", ] [[package]] @@ -3196,7 +3235,7 @@ dependencies = [ "primitive-types 0.10.1", "proptest", "proptest-derive", - "rand 0.8.4", + "rand 0.8.5", "ref-cast", "regex", "serde 1.0.145", @@ -3422,6 +3461,7 @@ dependencies = [ "parking_lot 0.12.1", "semver 1.0.14", "similar", + "solana_rbpf", ] [[package]] @@ -3494,7 +3534,7 @@ dependencies = [ "num 0.4.0", "once_cell", "pretty", - "rand 0.8.4", + "rand 0.8.5", "serde 1.0.145", "serde_json", "shell-words", @@ -3524,7 +3564,7 @@ dependencies = [ "num 0.4.0", "once_cell", "pretty", - "rand 0.8.4", + "rand 0.8.5", "regex", "serde 1.0.145", "serde_json", @@ -3705,7 +3745,7 @@ dependencies = [ "once_cell", "pretty", "primitive-types 0.10.1", - "rand 0.8.4", + "rand 0.8.5", "regex", "serde 1.0.145", "serde_json", @@ -4485,7 +4525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared", - "rand 0.8.4", + "rand 0.8.5", ] [[package]] @@ -4542,6 +4582,12 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "plotters" version = "0.3.0" @@ -4764,7 +4810,7 @@ dependencies = [ "lazy_static 1.4.0", "num-traits 0.2.14", "quick-error 2.0.0", - "rand 0.8.4", + "rand 0.8.5", "rand_chacha 0.3.0", "rand_xorshift", "regex-syntax", @@ -4921,19 +4967,18 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", + "rand_hc", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.0", "rand_core 0.6.2", - "rand_hc 0.3.0", ] [[package]] @@ -4983,15 +5028,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_hc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" -dependencies = [ - "rand_core 0.6.2", -] - [[package]] name = "rand_xorshift" version = "0.3.0" @@ -5239,6 +5275,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -5328,6 +5370,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" +dependencies = [ + "proc-macro2 1.0.43", + "quote 1.0.9", + "syn 1.0.99", +] + [[package]] name = "security-framework" version = "2.6.1" @@ -5736,6 +5798,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "solana_rbpf" +version = "0.2.38" +source = "git+https://github.com/brson/rbpf.git?rev=124c732056bd05d773ad48107ff1c33fb06dc989#124c732056bd05d773ad48107ff1c33fb06dc989" +dependencies = [ + "byteorder", + "combine 3.8.1", + "goblin", + "hash32", + "libc", + "log", + "rand 0.8.5", + "rustc-demangle", + "scroll", + "thiserror", + "winapi", +] + [[package]] name = "spec-flatten" version = "0.1.0" @@ -5923,7 +6003,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if 1.0.0", "libc", - "rand 0.8.4", + "rand 0.8.5", "redox_syscall 0.2.10", "remove_dir_all", "winapi", @@ -5943,7 +6023,7 @@ dependencies = [ "percent-encoding", "pest", "pest_derive", - "rand 0.8.4", + "rand 0.8.5", "regex", "serde 1.0.145", "serde_json", @@ -6002,7 +6082,7 @@ dependencies = [ "move-vm-types", "num_cpus", "once_cell", - "rand 0.8.4", + "rand 0.8.5", "tracing", "tracing-subscriber", ] @@ -6176,7 +6256,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" dependencies = [ - "combine", + "combine 4.6.4", "indexmap", "itertools 0.10.1", "serde 1.0.145", @@ -6310,7 +6390,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee73e6e4924fe940354b8d4d98cad5231175d615cd855b758adc658c0aac6a0" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "static_assertions", ] @@ -6467,6 +6547,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + [[package]] name = "url" version = "2.2.2" @@ -6530,6 +6619,12 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "vte" version = "0.10.1" diff --git a/Cargo.toml b/Cargo.toml index 931b759348..777757f50f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,3 +115,6 @@ inherits = "test" debug = 0 # for saving disk space during linking incremental = false codegen-units = 16 + +#[patch."https://github.com/brson/rbpf.git"] +#solana_rbpf.path = "../rbpf" diff --git a/language/tools/move-mv-llvm-compiler/Cargo.toml b/language/tools/move-mv-llvm-compiler/Cargo.toml index 8fecdc7a5d..a2e24cc1cb 100644 --- a/language/tools/move-mv-llvm-compiler/Cargo.toml +++ b/language/tools/move-mv-llvm-compiler/Cargo.toml @@ -34,8 +34,15 @@ extension-trait = "1.0.1" [dev-dependencies] datatest-stable = "0.1.1" +extension-trait = "1.0.1" similar = "2.1.0" +[dev-dependencies.solana_rbpf] +#git = "https://github.com/solana-labs/rbpf.git" +#rev = "c03dbfef82487396fc6f96d2cfeca409d6181192" +git = "https://github.com/brson/rbpf.git" +rev = "124c732056bd05d773ad48107ff1c33fb06dc989" + [features] default = [] @@ -46,3 +53,7 @@ harness = false [[test]] name = "move-ir-tests" harness = false + +[[test]] +name = "rbpf-tests" +harness = false diff --git a/language/tools/move-mv-llvm-compiler/src/cstr.rs b/language/tools/move-mv-llvm-compiler/src/cstr.rs index 5f5a01c87b..f4d8752b0d 100644 --- a/language/tools/move-mv-llvm-compiler/src/cstr.rs +++ b/language/tools/move-mv-llvm-compiler/src/cstr.rs @@ -31,7 +31,7 @@ fn cstr(s: &str) -> *const libc::c_char { // Allocate space for null byte, applied by CString::new let mut new_str = String::with_capacity(s.len() + 1); new_str.push_str(s); - let new_cstr = CString::new(new_str).expect("null byte"); + let new_cstr = CString::new(new_str).expect("interior nul byte"); let ptr = new_cstr.as_ptr(); map.insert(s.to_owned(), new_cstr); ptr diff --git a/language/tools/move-mv-llvm-compiler/src/main.rs b/language/tools/move-mv-llvm-compiler/src/main.rs index 767145fe68..fcd7c198f6 100644 --- a/language/tools/move-mv-llvm-compiler/src/main.rs +++ b/language/tools/move-mv-llvm-compiler/src/main.rs @@ -56,6 +56,10 @@ struct Args { /// Output llvm bitcode in a human readable text format. #[clap(short = 'S')] pub llvm_ir: bool, + + /// Output an object file + #[clap(short = 'O')] + pub obj: bool, } fn main() -> anyhow::Result<()> { @@ -136,6 +140,10 @@ fn main() -> anyhow::Result<()> { disassembler.llvm_write_to_file(module, args.llvm_ir, &args.output_file_path)?; */ + if args.llvm_ir && args.obj { + anyhow::bail!("can't output both LLVM IR (-S) and object file (-O)"); + } + { use move_mv_llvm_compiler::stackless::*; @@ -144,7 +152,16 @@ fn main() -> anyhow::Result<()> { let global_cx = GlobalContext::new(&model_env, Target::Solana); let mod_cx = global_cx.create_module_context(mod_id); let mut llmod = mod_cx.translate(); - llvm_write_to_file(llmod.as_mut(), args.llvm_ir, &args.output_file_path)?; + if !args.obj { + llvm_write_to_file(llmod.as_mut(), args.llvm_ir, &args.output_file_path)?; + drop(llmod); + } else { + write_object_file(llmod, Target::Solana, &args.output_file_path)?; + } + + // NB: context must outlive llvm module + // fixme this should be handled with lifetimes + drop(global_cx); }; Ok(()) diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs b/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs index 741c5a4aec..92da064cb6 100644 --- a/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs +++ b/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs @@ -12,14 +12,26 @@ use llvm_extra_sys::*; use llvm_sys::core::*; use llvm_sys::prelude::*; +use llvm_sys::target::*; +use llvm_sys::target_machine::*; use crate::cstr::SafeCStr; use std::ptr; +use std::ffi::{CStr, CString}; pub use llvm_extra_sys::AttributeKind; pub use llvm_sys::LLVMIntPredicate; +pub fn initialize_bpf() { + unsafe { + LLVMInitializeBPFTargetInfo(); + LLVMInitializeBPFTarget(); + LLVMInitializeBPFTargetMC(); + LLVMInitializeBPFAsmPrinter(); + } +} + pub struct Context(LLVMContextRef); impl Drop for Context { @@ -133,6 +145,16 @@ impl Module { ); } } + + pub fn set_data_layout(&self, machine: &TargetMachine) { + unsafe { + let target_data = LLVMCreateTargetDataLayout(machine.0); + let layout_str = LLVMCopyStringRepOfTargetData(target_data); + LLVMSetDataLayout(self.0, layout_str); + LLVMDisposeMessage(layout_str); + LLVMDisposeTargetData(target_data); + } + } } pub struct Builder(LLVMBuilderRef); @@ -357,3 +379,103 @@ impl Constant { unsafe { Constant(LLVMConstInt(ty.0, v, false as LLVMBool)) } } } + +pub struct Target(LLVMTargetRef); + +impl Target { + pub fn from_triple(triple: &str) -> anyhow::Result { + unsafe { + let target: &mut LLVMTargetRef = &mut ptr::null_mut(); + let error: &mut *mut libc::c_char = &mut ptr::null_mut(); + let result = LLVMGetTargetFromTriple( + triple.cstr(), + target, + error, + ); + + if result == 0 { + assert!((*error).is_null()); + Ok(Target(*target)) + } else { + assert!(!(*error).is_null()); + let rust_error = CStr::from_ptr(*error) + .to_str()?.to_string(); + LLVMDisposeMessage(*error); + anyhow::bail!("{rust_error}"); + } + } + } + + pub fn create_target_machine( + &self, + triple: &str, + cpu: &str, + features: &str, + ) -> TargetMachine { + unsafe { + // fixme some of these should be params + let level = LLVMCodeGenOptLevel::LLVMCodeGenLevelNone; + let reloc = LLVMRelocMode::LLVMRelocPIC; + let code_model = LLVMCodeModel::LLVMCodeModelDefault; + + let machine = LLVMCreateTargetMachine( + self.0, + triple.cstr(), + cpu.cstr(), + features.cstr(), + level, + reloc, + code_model, + ); + + TargetMachine(machine) + } + } +} + +pub struct TargetMachine(LLVMTargetMachineRef); + +impl Drop for TargetMachine { + fn drop(&mut self) { + unsafe { + LLVMDisposeTargetMachine(self.0); + } + } +} + +impl TargetMachine { + pub fn emit_to_obj_file( + &self, + module: &Module, + filename: &str, + ) -> anyhow::Result<()> { + unsafe { + // nb: llvm-sys seemingly-incorrectly wants + // a mutable c-string for the filename. + let filename = CString::new(filename.to_string()).expect("interior nul byte"); + let mut filename = filename.into_bytes_with_nul(); + let filename: *mut u8 = filename.as_mut_ptr(); + let filename = filename as *mut libc::c_char; + + let error: &mut *mut libc::c_char = &mut ptr::null_mut(); + let result = LLVMTargetMachineEmitToFile( + self.0, + module.0, + filename, + LLVMCodeGenFileType::LLVMObjectFile, + error, + ); + + if result == 0 { + assert!((*error).is_null()); + Ok(()) + } else { + assert!(!(*error).is_null()); + let rust_error = CStr::from_ptr(*error) + .to_str()?.to_string(); + LLVMDisposeMessage(*error); + anyhow::bail!("{rust_error}"); + } + } + } +} diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs b/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs index fa27d08940..aee14e2507 100644 --- a/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs +++ b/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs @@ -13,11 +13,31 @@ pub enum Target { } impl Target { - pub fn triple(&self) -> &'static str { + fn triple(&self) -> &'static str { match self { Target::Solana => "bpfel-unknown-unknown", } } + + fn llvm_cpu(&self) -> &'static str { + match self { + Target::Solana => "generic", + } + } + + fn llvm_features(&self) -> &'static str { + match self { + Target::Solana => "+solana", + } + } + + fn initialize_llvm(&self) { + match self { + Target::Solana => { + llvm::initialize_bpf(); + } + } + } } pub struct GlobalContext<'a> { @@ -28,6 +48,8 @@ pub struct GlobalContext<'a> { impl<'a> GlobalContext<'a> { pub fn new(env: &mm::GlobalEnv, target: Target) -> GlobalContext { + target.initialize_llvm(); + GlobalContext { env, llvm_cx: llvm::Context::new(), @@ -43,7 +65,7 @@ impl<'a> GlobalContext<'a> { llvm_cx: &self.llvm_cx, llvm_module: self.llvm_cx.create_module(&name), llvm_builder: self.llvm_cx.create_builder(), - target: self.target, + _target: self.target, } } } @@ -53,13 +75,11 @@ pub struct ModuleContext<'a> { llvm_cx: &'a llvm::Context, llvm_module: llvm::Module, llvm_builder: llvm::Builder, - target: Target, + _target: Target, } impl<'a> ModuleContext<'a> { pub fn translate(self) -> llvm::Module { - self.llvm_module.set_target(self.target.triple()); - let filename = self.env.get_source_path().to_str().expect("utf-8"); self.llvm_module.set_source_file_name(filename); @@ -432,3 +452,26 @@ mod exts { pub enum RtCall { Abort(mast::TempIndex), } + + +/// Compile the module to object file. +/// +/// This takes the module by value because it would otherwise have +/// side effects, mutating target-specific properties. +pub fn write_object_file(llmod: llvm::Module, target: Target, outpath: &str) -> anyhow::Result<()> { + let lltarget = llvm::Target::from_triple(target.triple())?; + let llmachine = lltarget.create_target_machine( + target.triple(), + target.llvm_cpu(), + target.llvm_features(), + ); + + llmod.set_target(target.triple()); + llmod.set_data_layout(&llmachine); + + llmod.verify(); + + llmachine.emit_to_obj_file(&llmod, outpath)?; + + Ok(()) +} diff --git a/language/tools/move-mv-llvm-compiler/tests/ir-tests.rs b/language/tools/move-mv-llvm-compiler/tests/ir-tests.rs index 8eddae21ba..db4ba05c2d 100644 --- a/language/tools/move-mv-llvm-compiler/tests/ir-tests.rs +++ b/language/tools/move-mv-llvm-compiler/tests/ir-tests.rs @@ -85,11 +85,8 @@ fn get_harness_paths() -> anyhow::Result { let move_mv_llvm_compiler = PathBuf::from(move_mv_llvm_compiler); // We have to guess where move-ir-compiler is - let move_ir_compiler = if !cfg!(windows) { - move_mv_llvm_compiler.with_file_name("move-ir-compiler") - } else { - move_mv_llvm_compiler.with_file_name("move-ir-compiler.exe") - }; + let move_ir_compiler = move_mv_llvm_compiler.with_file_name("move-ir-compiler") + .with_extension(std::env::consts::EXE_EXTENSION); if !move_ir_compiler.exists() { // todo: can we build move-ir-compiler automatically? @@ -181,7 +178,7 @@ fn compile_mvir_to_mvbc(harness_paths: &HarnessPaths, test_plan: &TestPlan) -> a cmd.arg("-m"); cmd.arg(test_plan.mvir_file.to_str().expect("PathBuf")); - let output = cmd.output().context("run move-ir-compiler failed")?; + let output = cmd.output()?; if !output.status.success() { anyhow::bail!( "move-ir-compiler failed. stderr:\n\n{}", diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests.rs b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests.rs index 0ccfabb8d9..790a9ff904 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests.rs +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests.rs @@ -44,12 +44,13 @@ //! //! - `// ignore` - don't run the test -use anyhow::Context; +use extension_trait::extension_trait; use similar::{ChangeTag, TextDiff}; -use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; + +mod test_common; +use test_common as tc; pub const TEST_DIR: &str = "tests/move-ir-tests"; @@ -60,16 +61,18 @@ fn run_test(test_path: &Path) -> Result<(), Box> { } fn run_test_inner(test_path: &Path) -> anyhow::Result<()> { - let harness_paths = get_harness_paths()?; - let test_plan = get_test_plan(test_path)?; + let harness_paths = tc::get_harness_paths()?; + let test_plan = tc::get_test_plan(test_path)?; if test_plan.should_ignore() { eprintln!("ignoring {}", test_plan.name); return Ok(()); } - run_move_build(&harness_paths, &test_plan)?; - let compilation_units = find_compilation_units(&test_plan)?; + tc::run_move_build(&harness_paths, &test_plan)?; + + let compilation_units = tc::find_compilation_units(&test_plan)?; + compile_all_bytecode_to_llvm_ir(&harness_paths, &compilation_units)?; maybe_promote_actual_llvm_ir_to_expected(&compilation_units)?; compare_all_actual_llvm_ir_to_expected(&compilation_units)?; @@ -77,216 +80,45 @@ fn run_test_inner(test_path: &Path) -> anyhow::Result<()> { Ok(()) } -#[derive(Debug)] -struct HarnessPaths { - move_build: PathBuf, - move_mv_llvm_compiler: PathBuf, -} - -fn get_harness_paths() -> anyhow::Result { - // Cargo will tell us the location of move-mv-llvm-compiler. - let move_mv_llvm_compiler = env!("CARGO_BIN_EXE_move-mv-llvm-compiler"); - let move_mv_llvm_compiler = PathBuf::from(move_mv_llvm_compiler); - - // We have to guess where move-ir-compiler is - let move_build = if !cfg!(windows) { - move_mv_llvm_compiler.with_file_name("move-build") - } else { - move_mv_llvm_compiler.with_file_name("move-build.exe") - }; - - if !move_build.exists() { - // todo: can we build move-build automatically? - - let is_release = move_build.to_string_lossy().contains("release"); - let suggestion = if is_release { - "try running `cargo build -p move-compiler --release` first" - } else { - "try running `cargo build -p move-compiler` first" - }; - anyhow::bail!("move-build not built. {suggestion}"); - } - - Ok(HarnessPaths { - move_build, - move_mv_llvm_compiler, - }) -} - -#[derive(Debug)] -struct TestPlan { - name: String, - /// The move file to be compiled to LLVM IR - move_file: PathBuf, - /// The build directory, which contains bytecode for multiple modules and - /// scripts. - build_dir: PathBuf, - /// Special commands embedded in the test file as comments - directives: Vec, -} - -#[derive(Debug, Eq, PartialEq)] -enum TestDirective { - Ignore, -} - -impl TestPlan { - fn should_ignore(&self) -> bool { - self.directives.contains(&TestDirective::Ignore) - } -} - -#[derive(Debug)] -struct CompilationUnit { - type_: CompilationUnitType, - bytecode: PathBuf, - llvm_ir: PathBuf, - llvm_ir_expected: PathBuf, -} - -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -enum CompilationUnitType { - Script, - Module, -} - -fn get_test_plan(test_path: &Path) -> anyhow::Result { - let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("cargo_manifest_dir"); - let move_file = Path::new(&manifest_dir).join(test_path); - - let name = test_path.to_string_lossy().to_string(); - let move_file = move_file.to_owned(); - let stem = move_file.file_stem().expect("stem").to_string_lossy(); - let build_dir = move_file.with_file_name(format!("{}-build", stem)); - let directives = load_directives(test_path)?; - - Ok(TestPlan { - name, - move_file, - build_dir, - directives, - }) -} - -fn load_directives(test_path: &Path) -> anyhow::Result> { - let mut directives = Vec::new(); - let source = std::fs::read_to_string(test_path)?; - - for line in source.lines() { - let line = line.trim(); - let line_is_comment = line.starts_with("//"); - if !line_is_comment { - continue; - } - let line = &line[2..].trim(); - if line.starts_with("ignore") { - directives.push(TestDirective::Ignore); - } - } - - Ok(directives) -} - -fn run_move_build(harness_paths: &HarnessPaths, test_plan: &TestPlan) -> anyhow::Result<()> { - let mut cmd = Command::new(&harness_paths.move_build); - cmd.arg(&test_plan.move_file); - cmd.args(["--flavor", "none"]); - cmd.args(["--out-dir", &test_plan.build_dir.to_str().expect("utf-8")]); - - let output = cmd.output().context("run move-build failed")?; - if !output.status.success() { - anyhow::bail!( - "move-build failed. stderr:\n\n{}", - String::from_utf8_lossy(&output.stderr) - ); +#[extension_trait] +impl CompilationUnitExt for tc::CompilationUnit { + fn llvm_ir_actual(&self) -> PathBuf { + self.bytecode.with_extension("actual.ll") } - Ok(()) -} - -fn find_compilation_units(test_plan: &TestPlan) -> anyhow::Result> { - let modules_dir = test_plan.build_dir.join("modules"); - let scripts_dir = test_plan.build_dir.join("scripts"); - - let dirs = [ - (modules_dir, CompilationUnitType::Module), - (scripts_dir, CompilationUnitType::Script), - ]; - - let mut units = vec![]; - - for (dir, type_) in dirs { - if !dir.exists() { - continue; - } - - for dirent in fs::read_dir(&dir)? { - let dirent = dirent?; - let path = dirent.path(); - if path.extension() != Some(&OsStr::new("mv")) { - continue; - } - - let bytecode = path; - let llvm_ir = bytecode.with_extension("actual.ll"); - let llvm_ir_expected = bytecode.with_extension("expected.ll"); - - units.push(CompilationUnit { - type_, - bytecode, - llvm_ir, - llvm_ir_expected, - }); - } + fn llvm_ir_expected(&self) -> PathBuf { + self.bytecode.with_extension("expected.ll") } - - Ok(units) } fn compile_all_bytecode_to_llvm_ir( - harness_paths: &HarnessPaths, - compilation_units: &[CompilationUnit], + harness_paths: &tc::HarnessPaths, + compilation_units: &[tc::CompilationUnit], ) -> anyhow::Result<()> { - for cu in compilation_units { - let mut cmd = Command::new(&harness_paths.move_mv_llvm_compiler); - cmd.arg("-b"); - cmd.arg(&cu.bytecode); - cmd.arg("-o"); - cmd.arg(&cu.llvm_ir); - cmd.arg("-S"); - - if cu.type_ == CompilationUnitType::Script { - cmd.arg("-s"); - } - - let output = cmd.output().context("run move-mv-llvm-compiler failed")?; - if !output.status.success() { - anyhow::bail!( - "move-mv-llvm-compiler failed. stderr:\n\n{}", - String::from_utf8_lossy(&output.stderr) - ); - } - } - - Ok(()) + tc::compile_all_bytecode( + harness_paths, + compilation_units, + "-S", + &|cu| cu.llvm_ir_actual() + ) } fn maybe_promote_actual_llvm_ir_to_expected( - compilation_units: &[CompilationUnit], + compilation_units: &[tc::CompilationUnit], ) -> anyhow::Result<()> { if !std::env::var("PROMOTE_LLVM_IR").is_ok() { return Ok(()); } for cu in compilation_units { - fs::copy(&cu.llvm_ir, &cu.llvm_ir_expected)?; + fs::copy(&cu.llvm_ir_actual(), &cu.llvm_ir_expected())?; } Ok(()) } fn compare_all_actual_llvm_ir_to_expected( - compilation_units: &[CompilationUnit], + compilation_units: &[tc::CompilationUnit], ) -> anyhow::Result<()> { for cu in compilation_units { compare_actual_llvm_ir_to_expected(cu)?; @@ -295,17 +127,17 @@ fn compare_all_actual_llvm_ir_to_expected( Ok(()) } -fn compare_actual_llvm_ir_to_expected(compilation_unit: &CompilationUnit) -> anyhow::Result<()> { - if !compilation_unit.llvm_ir_expected.exists() { +fn compare_actual_llvm_ir_to_expected(compilation_unit: &tc::CompilationUnit) -> anyhow::Result<()> { + if !compilation_unit.llvm_ir_expected().exists() { anyhow::bail!( "no expected.ll file: {:?}", - compilation_unit.llvm_ir_expected + compilation_unit.llvm_ir_expected() ); } let mut diff_msg = String::new(); - let file_actual = fs::read_to_string(&compilation_unit.llvm_ir)?; - let file_expected = fs::read_to_string(&compilation_unit.llvm_ir_expected)?; + let file_actual = fs::read_to_string(&compilation_unit.llvm_ir_actual())?; + let file_expected = fs::read_to_string(&compilation_unit.llvm_ir_expected())?; let diff = TextDiff::from_lines(&file_expected, &file_actual); for change in diff.iter_all_changes() { @@ -323,7 +155,7 @@ fn compare_actual_llvm_ir_to_expected(compilation_unit: &CompilationUnit) -> any if !diff_msg.is_empty() { anyhow::bail!(format!( "LLVM IR actual ({:?}) does not equal expected: \n\n{}", - compilation_unit.llvm_ir, diff_msg + compilation_unit.llvm_ir_actual(), diff_msg )); } diff --git a/language/tools/move-mv-llvm-compiler/tests/rbpf-tests.rs b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests.rs new file mode 100644 index 0000000000..2772ca3393 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests.rs @@ -0,0 +1,184 @@ +use anyhow::Context; +use extension_trait::extension_trait; +use std::path::{Path, PathBuf}; +use std::process::Command; +use solana_rbpf as rbpf; + +mod test_common; +use test_common as tc; + +pub const TEST_DIR: &str = "tests/rbpf-tests"; + +datatest_stable::harness!(run_test, TEST_DIR, r".*\.move"); + +fn run_test(test_path: &Path) -> Result<(), Box> { + Ok(run_test_inner(test_path)?) +} + +fn run_test_inner(test_path: &Path) -> anyhow::Result<()> { + let bpf_tools = get_bpf_tools()?; + + let harness_paths = tc::get_harness_paths()?; + let test_plan = tc::get_test_plan(test_path)?; + + if test_plan.should_ignore() { + eprintln!("ignoring {}", test_plan.name); + return Ok(()); + } + + tc::run_move_build(&harness_paths, &test_plan)?; + + let compilation_units = tc::find_compilation_units(&test_plan)?; + + compile_all_bytecode_to_object_files(&harness_paths, &compilation_units)?; + + let exe = link_object_files(&test_plan, &bpf_tools, &compilation_units)?; + + run_rbpf(&exe)?; + + Ok(()) +} + +#[extension_trait] +impl CompilationUnitExt for tc::CompilationUnit { + fn object_file(&self) -> PathBuf { + self.bytecode.with_extension("o") + } +} + +fn compile_all_bytecode_to_object_files( + harness_paths: &tc::HarnessPaths, + compilation_units: &[tc::CompilationUnit], +) -> anyhow::Result<()> { + tc::compile_all_bytecode( + harness_paths, + compilation_units, + "-O", + &|cu| cu.object_file() + ) +} + +struct BpfTools { + _root: PathBuf, + clang: PathBuf, + rustc: PathBuf, + lld: PathBuf, +} + +fn get_bpf_tools() -> anyhow::Result { + let bpf_tools_root = std::env::var("BPF_TOOLS_ROOT") + .context("env var BPF_TOOLS_ROOT not set")?; + let bpf_tools_root = PathBuf::from(bpf_tools_root); + + let bpf_tools = BpfTools { + _root: bpf_tools_root.clone(), + clang: bpf_tools_root.join("llvm/bin/clang") + .with_extension(std::env::consts::EXE_EXTENSION), + rustc: bpf_tools_root.join("rust/bin/rustc") + .with_extension(std::env::consts::EXE_EXTENSION), + lld: bpf_tools_root.join("llvm/bin/ld.lld"), + }; + + if !bpf_tools.clang.exists() { + anyhow::bail!("no clang bin at {}", bpf_tools.clang.display()); + } + if !bpf_tools.rustc.exists() { + anyhow::bail!("no rustc bin at {}", bpf_tools.rustc.display()); + } + if !bpf_tools.lld.exists() { + anyhow::bail!("no lld bin at {}", bpf_tools.lld.display()); + } + + Ok(bpf_tools) +} + +#[allow(unused)] +fn link_object_files_(test_plan: &tc::TestPlan, bpf_tools: &BpfTools, compilation_units: &[tc::CompilationUnit]) -> anyhow::Result { + let output_dylib = test_plan.build_dir.join("output.so"); + + let mut cmd = Command::new(&bpf_tools.clang); + //cmd.arg("--target=bpfel-unknown-unknown"); + cmd.args(["-target", "bpf"]); + cmd.arg("-fPIC"); + cmd.arg("-march=bpfel+solana"); + cmd.arg(format!("-fuse-ld={}", bpf_tools.lld.display())); + cmd.arg("-shared"); // create a shared library + cmd.arg("-o"); + cmd.arg(&output_dylib); + cmd.arg("-v"); + + for cu in compilation_units { + cmd.arg(&cu.object_file()); + } + + let output = cmd.output()?; + if !output.status.success() { + anyhow::bail!( + "linking with lld failed. stderr:\n\n{}", + String::from_utf8_lossy(&output.stderr) + ); + } + + Ok(output_dylib) +} + +fn link_object_files(test_plan: &tc::TestPlan, bpf_tools: &BpfTools, compilation_units: &[tc::CompilationUnit]) -> anyhow::Result { + let output_dylib = test_plan.build_dir.join("output.so"); + + let mut cmd = Command::new(&bpf_tools.lld); + cmd.args(["-z", "notext"]); + cmd.arg("-shared"); + cmd.arg("--Bdynamic"); + cmd.args(["--entry", "main"]); + cmd.arg("-o"); + cmd.arg(&output_dylib); + + for cu in compilation_units { + cmd.arg(&cu.object_file()); + } + + let output = cmd.output()?; + if !output.status.success() { + anyhow::bail!( + "linking with lld failed. stderr:\n\n{}", + String::from_utf8_lossy(&output.stderr) + ); + } + + Ok(output_dylib) +} + +fn run_rbpf(exe: &Path) -> anyhow::Result<()> { + use rbpf::vm::*; + use rbpf::memory_region::MemoryRegion; + use rbpf::elf::Executable; + use rbpf::ebpf; + use rbpf::verifier::RequisiteVerifier; + //use rbpf::elf_parser_glue::{GoblinParser, ElfParser}; + use std::sync::Arc; + + let elf = &std::fs::read(exe)?; + //let parser = GoblinParser::parse(elf)?; + let mem = &mut vec![0; 1024]; + + let config = Config { + dynamic_stack_frames: false, + enable_elf_vaddr: false, + reject_rodata_stack_overlap: false, + static_syscalls: false, + optimize_rodata: false, + new_elf_parser: true, + .. Config::default() + }; + let loader = Arc::new(BuiltInProgram::new_loader(config)); + //let function_registry = FunctionRegistry::default(); + let executable = Executable::::from_elf(elf, loader).unwrap(); + let mem_region = MemoryRegion::new_writable(mem, ebpf::MM_INPUT_START); + let verified_executable = VerifiedExecutable::::from_executable(executable).unwrap(); + let mut context_object = TestContextObject::new(1); + let mut vm = EbpfVm::new(&verified_executable, &mut context_object, &mut [], vec![mem_region]).unwrap(); + + let (_instruction_count, _result) = vm.execute_program(true); + + Ok(()) +} diff --git a/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/script-build/scripts/main.mv b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/script-build/scripts/main.mv new file mode 100644 index 0000000000000000000000000000000000000000..a76c05de9aca86feb46ad9a540fd6ac334f9bc06 GIT binary patch literal 18 YcmZ1|^O~EDfq{XMm4OjRF)%U#039m anyhow::Result { + // Cargo will tell us the location of move-mv-llvm-compiler. + let move_mv_llvm_compiler = env!("CARGO_BIN_EXE_move-mv-llvm-compiler"); + let move_mv_llvm_compiler = PathBuf::from(move_mv_llvm_compiler); + + // We have to guess where move-ir-compiler is + let move_build = move_mv_llvm_compiler.with_file_name("move-build") + .with_extension(std::env::consts::EXE_EXTENSION); + + if !move_build.exists() { + // todo: can we build move-build automatically? + + let is_release = move_build.to_string_lossy().contains("release"); + let suggestion = if is_release { + "try running `cargo build -p move-compiler --release` first" + } else { + "try running `cargo build -p move-compiler` first" + }; + anyhow::bail!("move-build not built. {suggestion}"); + } + + Ok(HarnessPaths { + move_build, + move_mv_llvm_compiler, + }) +} + +#[derive(Debug)] +pub struct TestPlan { + pub name: String, + /// The move file to be compiled to LLVM IR + pub move_file: PathBuf, + /// The build directory, which contains bytecode for multiple modules and + /// scripts. + pub build_dir: PathBuf, + /// Special commands embedded in the test file as comments + pub directives: Vec, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum TestDirective { + Ignore, +} + +impl TestPlan { + pub fn should_ignore(&self) -> bool { + self.directives.contains(&TestDirective::Ignore) + } +} + +pub fn get_test_plan(test_path: &Path) -> anyhow::Result { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("cargo_manifest_dir"); + let move_file = Path::new(&manifest_dir).join(test_path); + + let name = test_path.to_string_lossy().to_string(); + let move_file = move_file.to_owned(); + let stem = move_file.file_stem().expect("stem").to_string_lossy(); + let build_dir = move_file.with_file_name(format!("{}-build", stem)); + let directives = load_directives(test_path)?; + + Ok(TestPlan { + name, + move_file, + build_dir, + directives, + }) +} + +fn load_directives(test_path: &Path) -> anyhow::Result> { + let mut directives = Vec::new(); + let source = std::fs::read_to_string(test_path)?; + + for line in source.lines() { + let line = line.trim(); + let line_is_comment = line.starts_with("//"); + if !line_is_comment { + continue; + } + let line = &line[2..].trim(); + if line.starts_with("ignore") { + directives.push(TestDirective::Ignore); + } + } + + Ok(directives) +} + +pub fn run_move_build(harness_paths: &HarnessPaths, test_plan: &TestPlan) -> anyhow::Result<()> { + let mut cmd = Command::new(&harness_paths.move_build); + cmd.arg(&test_plan.move_file); + cmd.args(["--flavor", "none"]); + cmd.args(["--out-dir", &test_plan.build_dir.to_str().expect("utf-8")]); + + let output = cmd.output()?; + if !output.status.success() { + anyhow::bail!( + "move-build failed. stderr:\n\n{}", + String::from_utf8_lossy(&output.stderr) + ); + } + + Ok(()) +} + +#[derive(Debug)] +pub struct CompilationUnit { + pub type_: CompilationUnitType, + pub bytecode: PathBuf, +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub enum CompilationUnitType { + Script, + Module, +} + +pub fn find_compilation_units(test_plan: &TestPlan) -> anyhow::Result> { + let modules_dir = test_plan.build_dir.join("modules"); + let scripts_dir = test_plan.build_dir.join("scripts"); + + let dirs = [ + (modules_dir, CompilationUnitType::Module), + (scripts_dir, CompilationUnitType::Script), + ]; + + let mut units = vec![]; + + for (dir, type_) in dirs { + if !dir.exists() { + continue; + } + + for dirent in fs::read_dir(&dir)? { + let dirent = dirent?; + let path = dirent.path(); + if path.extension() != Some(&OsStr::new("mv")) { + continue; + } + + let bytecode = path; + + units.push(CompilationUnit { + type_, + bytecode, + }); + } + } + + Ok(units) +} + +pub fn compile_all_bytecode( + harness_paths: &HarnessPaths, + compilation_units: &[CompilationUnit], + outtype_flag: &str, + outfile: &dyn Fn(&CompilationUnit) -> PathBuf, +) -> anyhow::Result<()> { + for cu in compilation_units { + let mut cmd = Command::new(&harness_paths.move_mv_llvm_compiler); + cmd.arg("-b"); + cmd.arg(&cu.bytecode); + cmd.arg("-o"); + cmd.arg(&outfile(&cu)); + cmd.arg(&outtype_flag); + + if cu.type_ == CompilationUnitType::Script { + cmd.arg("-s"); + } + + let output = cmd.output().context("run move-mv-llvm-compiler failed")?; + if !output.status.success() { + anyhow::bail!( + "move-mv-llvm-compiler failed. stderr:\n\n{}", + String::from_utf8_lossy(&output.stderr) + ); + } + } + + Ok(()) +} From 9d304f1b85c6549056df6e9609a7c6c17fdf1da5 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 30 Jan 2023 14:22:19 -0700 Subject: [PATCH 3/5] Update readme for new test suite --- .../tools/move-mv-llvm-compiler/README.md | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/language/tools/move-mv-llvm-compiler/README.md b/language/tools/move-mv-llvm-compiler/README.md index 348c77462a..4b99f4bed5 100644 --- a/language/tools/move-mv-llvm-compiler/README.md +++ b/language/tools/move-mv-llvm-compiler/README.md @@ -13,16 +13,34 @@ Compile move-bytecode to llvm bitcode The llvm-compiler uses [Inkwell](https://github.com/TheDan64/inkwell) (Safe Rust bindings to LLVM's C API) to generate llvm bitcode for Move IR. -## Dependencies: -- llvm-project -- inkwell +## Setup + +Building requires a local build of [`llvm-project`](https://github.com/solana-labs/llvm-project) +from Solana's fork that supports the Solana variant of eBPF, +and testing requires an installation of the Solana [`bpf-tools`](https://github.com/solana-labs/bpf-tools). + +Known working revisions of both: + +- llvm-project: commit `a0bf4d22b6af79f5f4d9a0b42ac3ef855e79b602`, + tag `15.0-2022-08-09`, + from the `solana-labs` repo +- bpf-tools: version `1.32` + +`bpf-tools` can be extracted from the binary release. + +Export two environment variables: + +- `LLVM_SYS_150_PREFIX` - the path to the LLVM build directory +- `BPF_TOOLS_ROOT` - the path at which `bpf-tools` was extracted + ## Testing -This project contains two test suites: `ir-tests` and `move-ir-tests`. -The first converts Move IR (`.mvir`) to LLVM IR, -and the second converts Move source (`.move`) to LLVM IR. -Both check the results against a reference IR file. +This project contains three test suites: + +- `ir-tests` - converts Move IR (`.mvir`) to LLVM IR, +- `move-ir-tests` - converts Move source (`.move`) to LLVM IR, +- `rbpf-tests` - runs move as BPF in the `rbpf` VM. These test require the `move-ir-compiler` and `move-build` tools, which can be built with @@ -31,27 +49,27 @@ which can be built with cargo build -p move-ir-compiler && cargo build -p move-compiler ``` -Run the tests with either - -``` -cargo test -p move-mv-llvm-compiler --test ir-tests` -``` +If you forget, the test harness will remind you what commands to run to build the tools. -or +Run the tests with any of these commands: ``` -cargo test -p move-vm-llvm-compiler --test move-ir-tests` +cargo test -p move-mv-llvm-compiler --test ir-tests +cargo test -p move-vm-llvm-compiler --test move-ir-tests +cargo test -p move-vm-llvm-compiler --test rbpf-tests ``` -These tests work by producing `.actual.ll` files and comparing them to +The IR tests work by producing `.actual.ll` files and comparing them to `.expected.ll` files. When introducing new tests, or making changes to the code generator that invalidate existing tests, the "actual" files need to be promoted to "expected" files. This can be done like ``` -PROMOTE_LLVM_IR=1 cargo test -p move-vm-llvm-compiler --test move-ir-tests` +PROMOTE_LLVM_IR=1 cargo test -p move-vm-llvm-compiler --test move-ir-tests ``` +Most new tests should be `move-ir-tests` or `rbpf-tests`, +as the Move IR is not stable nor easy to work with. ### TODO From 1307209e20855e8ca513fa9188e7b866f7214e3b Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 30 Jan 2023 14:22:26 -0700 Subject: [PATCH 4/5] Update .expected.ll --- .../tests/ir-tests/empty-module.expected.ll | 9 ++------- .../tests/ir-tests/friend.expected.ll | 9 ++------- .../tests/ir-tests/struct-pair.expected.ll | 15 ++------------- .../tests/ir-tests/struct.expected.ll | 13 ++----------- .../tests/ir-tests/struct_arguments.expected.ll | 13 ++----------- .../abort-build/modules/0_Test.expected.ll | 1 - .../assert-build/modules/0_Test.expected.ll | 1 - .../assign-build/scripts/main.expected.ll | 1 - .../modules/0_Empty.expected.ll | 1 - .../eq-build/modules/0_Test.expected.ll | 1 - .../if-build/modules/0_Test.expected.ll | 1 - .../math_u8-build/modules/0_Test.expected.ll | 1 - .../return-build/modules/0_Test.expected.ll | 1 - .../script-build/scripts/main.expected.ll | 1 - 14 files changed, 10 insertions(+), 58 deletions(-) diff --git a/language/tools/move-mv-llvm-compiler/tests/ir-tests/empty-module.expected.ll b/language/tools/move-mv-llvm-compiler/tests/ir-tests/empty-module.expected.ll index 62a9d5e665..60b7ece722 100644 --- a/language/tools/move-mv-llvm-compiler/tests/ir-tests/empty-module.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/ir-tests/empty-module.expected.ll @@ -1,7 +1,2 @@ -; ModuleID = 'module 1.TestBinaryOps' -source_filename = "1.TestBinaryOps.bc" -target triple = "bpfel-unknown-unknown" - -!llvm.module.flags = !{!0} - -!0 = !{i32 2, !"Debug Info Version", i64 3} +; ModuleID = '0x1__TestBinaryOps' +source_filename = "" diff --git a/language/tools/move-mv-llvm-compiler/tests/ir-tests/friend.expected.ll b/language/tools/move-mv-llvm-compiler/tests/ir-tests/friend.expected.ll index 80bdc42b36..a171a06fb7 100644 --- a/language/tools/move-mv-llvm-compiler/tests/ir-tests/friend.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/ir-tests/friend.expected.ll @@ -1,7 +1,2 @@ -; ModuleID = 'module 42.A' -source_filename = "42.A.bc" -target triple = "bpfel-unknown-unknown" - -!llvm.module.flags = !{!0} - -!0 = !{i32 2, !"Debug Info Version", i64 3} +; ModuleID = '0x42__A' +source_filename = "" diff --git a/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct-pair.expected.ll b/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct-pair.expected.ll index 9749d4bc07..60b7ece722 100644 --- a/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct-pair.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct-pair.expected.ll @@ -1,13 +1,2 @@ -; ModuleID = 'module 1.TestBinaryOps' -source_filename = "1.TestBinaryOps.bc" -target triple = "bpfel-unknown-unknown" - -%T = type { i1 } -%Pair = type { i64, i64 } - -@T = external global %T -@Pair = external global %Pair - -!llvm.module.flags = !{!0} - -!0 = !{i32 2, !"Debug Info Version", i64 3} +; ModuleID = '0x1__TestBinaryOps' +source_filename = "" diff --git a/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct.expected.ll b/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct.expected.ll index 2300996223..60b7ece722 100644 --- a/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct.expected.ll @@ -1,11 +1,2 @@ -; ModuleID = 'module 1.TestBinaryOps' -source_filename = "1.TestBinaryOps.bc" -target triple = "bpfel-unknown-unknown" - -%T = type { i1 } - -@T = external global %T - -!llvm.module.flags = !{!0} - -!0 = !{i32 2, !"Debug Info Version", i64 3} +; ModuleID = '0x1__TestBinaryOps' +source_filename = "" diff --git a/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct_arguments.expected.ll b/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct_arguments.expected.ll index 32edca61f8..0ee40234e2 100644 --- a/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct_arguments.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/ir-tests/struct_arguments.expected.ll @@ -1,11 +1,2 @@ -; ModuleID = 'module 42.M' -source_filename = "42.M.bc" -target triple = "bpfel-unknown-unknown" - -%S = type { i64 } - -@S = external global %S - -!llvm.module.flags = !{!0} - -!0 = !{i32 2, !"Debug Info Version", i64 3} +; ModuleID = '0x42__M' +source_filename = "" diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll index 61452b9ba5..4d4e79a967 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll @@ -1,6 +1,5 @@ ; ModuleID = '0x100__Test' source_filename = "" -target triple = "bpfel-unknown-unknown" define void @test() { entry_: diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll index 61452b9ba5..4d4e79a967 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll @@ -1,6 +1,5 @@ ; ModuleID = '0x100__Test' source_filename = "" -target triple = "bpfel-unknown-unknown" define void @test() { entry_: diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll index ca67807b3c..fd244a9bd7 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll @@ -1,6 +1,5 @@ ; ModuleID = '' source_filename = "" -target triple = "bpfel-unknown-unknown" define void @main() { entry_: diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/empty-module-build/modules/0_Empty.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/empty-module-build/modules/0_Empty.expected.ll index 90f1121dc8..a6313e79ce 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/empty-module-build/modules/0_Empty.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/empty-module-build/modules/0_Empty.expected.ll @@ -1,3 +1,2 @@ ; ModuleID = '0x100__Empty' source_filename = "" -target triple = "bpfel-unknown-unknown" diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll index fcfead0df1..59c4802bea 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll @@ -1,6 +1,5 @@ ; ModuleID = '0x100__Test' source_filename = "" -target triple = "bpfel-unknown-unknown" define i1 @test(i8 %0, i8 %1) { entry_: diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll index 6657fea8b5..cabfcc3fce 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll @@ -1,6 +1,5 @@ ; ModuleID = '0x100__Test' source_filename = "" -target triple = "bpfel-unknown-unknown" define i8 @test(i1 %0) { entry_: diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll index 71b13eb6b5..b0c4684b40 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll @@ -1,6 +1,5 @@ ; ModuleID = '0x100__Test' source_filename = "" -target triple = "bpfel-unknown-unknown" define i8 @test(i8 %0, i8 %1) { entry_: diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll index 7b67e51e9b..d971962be5 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll @@ -1,6 +1,5 @@ ; ModuleID = '0x100__Test' source_filename = "" -target triple = "bpfel-unknown-unknown" define i8 @test() { entry_: diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll index 0b766c1cf8..bcc4ed7b65 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll @@ -1,6 +1,5 @@ ; ModuleID = '' source_filename = "" -target triple = "bpfel-unknown-unknown" define void @main() { entry_: From abe480d75fb4a7ef07245de2f68699c94e308594 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 30 Jan 2023 14:24:56 -0700 Subject: [PATCH 5/5] Cleanup --- .gitignore | 1 + Cargo.toml | 3 - .../tools/move-mv-llvm-compiler/Cargo.toml | 4 +- .../llvm-extra-sys/Cargo.toml | 3 + .../llvm-extra-sys/src/lib.rs | 4 + .../llvm-extra-sys/src/llvm-extra.cpp | 20 +- .../tools/move-mv-llvm-compiler/src/cstr.rs | 6 + .../tools/move-mv-llvm-compiler/src/main.rs | 15 +- .../src/stackless/extensions.rs | 26 + .../src/stackless/llvm.rs | 67 +-- .../src/stackless/mod.rs | 478 +---------------- .../src/stackless/translate.rs | 481 ++++++++++++++++++ .../abort-build/modules/0_Test.expected.ll | 10 +- .../assert-build/modules/0_Test.expected.ll | 10 +- .../assign-build/scripts/main.expected.ll | 6 +- .../eq-build/modules/0_Test.expected.ll | 36 +- .../if-build/modules/0_Test.expected.ll | 54 +- .../math_u8-build/modules/0_Test.expected.ll | 36 +- .../return-build/modules/0_Test.expected.ll | 10 +- .../script-build/scripts/main.expected.ll | 2 +- .../tests/move-ir-tests/script.move | 1 - .../move-mv-llvm-compiler/tests/rbpf-tests.rs | 46 +- .../rbpf-tests/script-build/scripts/main.mv | Bin 18 -> 0 bytes 23 files changed, 639 insertions(+), 680 deletions(-) create mode 100644 language/tools/move-mv-llvm-compiler/src/stackless/extensions.rs create mode 100644 language/tools/move-mv-llvm-compiler/src/stackless/translate.rs delete mode 100644 language/tools/move-mv-llvm-compiler/tests/rbpf-tests/script-build/scripts/main.mv diff --git a/.gitignore b/.gitignore index d59c46164e..7b351f8b86 100644 --- a/.gitignore +++ b/.gitignore @@ -59,5 +59,6 @@ target-out-docker **/move-mv-llvm-compiler/**/ir-tests/*.mv **/move-mv-llvm-compiler/**/move-ir-tests/**/*.mv **/move-mv-llvm-compiler/**/move-ir-tests/**/*.actual.ll +**/move-mv-llvm-compiler/**/rbpf-tests/**/*.mv **/move-mv-llvm-compiler/**/rbpf-tests/**/*.o **/move-mv-llvm-compiler/**/rbpf-tests/**/*.so diff --git a/Cargo.toml b/Cargo.toml index 777757f50f..931b759348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,6 +115,3 @@ inherits = "test" debug = 0 # for saving disk space during linking incremental = false codegen-units = 16 - -#[patch."https://github.com/brson/rbpf.git"] -#solana_rbpf.path = "../rbpf" diff --git a/language/tools/move-mv-llvm-compiler/Cargo.toml b/language/tools/move-mv-llvm-compiler/Cargo.toml index a2e24cc1cb..eebdf02f01 100644 --- a/language/tools/move-mv-llvm-compiler/Cargo.toml +++ b/language/tools/move-mv-llvm-compiler/Cargo.toml @@ -34,12 +34,10 @@ extension-trait = "1.0.1" [dev-dependencies] datatest-stable = "0.1.1" -extension-trait = "1.0.1" similar = "2.1.0" [dev-dependencies.solana_rbpf] -#git = "https://github.com/solana-labs/rbpf.git" -#rev = "c03dbfef82487396fc6f96d2cfeca409d6181192" +# nb: this fork is carrying a minor workaround for an undiagnosed issue with how how are linking bpf git = "https://github.com/brson/rbpf.git" rev = "124c732056bd05d773ad48107ff1c33fb06dc989" diff --git a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/Cargo.toml b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/Cargo.toml index 31f2a6906c..f6a3818a4b 100644 --- a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/Cargo.toml +++ b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "llvm-extra-sys" version = "0.1.0" +authors = ["Diem Association "] +description = "Additional LLVM glue" license = "Apache 2.0 / MIT" +publish = false edition = "2021" [dependencies] diff --git a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/lib.rs b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/lib.rs index 9e499dd428..db561f2353 100644 --- a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/lib.rs +++ b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/lib.rs @@ -1,3 +1,7 @@ +//! Access to LLVM features not provided by the C API. +//! +//! Most or all copied from Rust. + #![allow(non_snake_case)] use libc::{c_uint, size_t}; diff --git a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/llvm-extra.cpp b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/llvm-extra.cpp index 64e0f1e487..5aee7ed25d 100644 --- a/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/llvm-extra.cpp +++ b/language/tools/move-mv-llvm-compiler/llvm-extra-sys/src/llvm-extra.cpp @@ -1,24 +1,6 @@ -// Most of this is copied from Rust +// Most or all copied from Rust -#include "llvm-c/Core.h" - -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/DebugInfoMetadata.h" -#include "llvm/IR/DiagnosticHandler.h" -#include "llvm/IR/DiagnosticInfo.h" -#include "llvm/IR/DiagnosticPrinter.h" -#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" -#include "llvm/IR/Intrinsics.h" -#include "llvm/IR/Mangler.h" -#include "llvm/Object/Archive.h" -#include "llvm/Object/COFFImportFile.h" -#include "llvm/Object/ObjectFile.h" -#include "llvm/Pass.h" -#include "llvm/Bitcode/BitcodeWriterPass.h" -#include "llvm/Support/Signals.h" -#include "llvm/ADT/Optional.h" using namespace llvm; using namespace llvm::sys; diff --git a/language/tools/move-mv-llvm-compiler/src/cstr.rs b/language/tools/move-mv-llvm-compiler/src/cstr.rs index f4d8752b0d..bf5ad59662 100644 --- a/language/tools/move-mv-llvm-compiler/src/cstr.rs +++ b/language/tools/move-mv-llvm-compiler/src/cstr.rs @@ -1,4 +1,10 @@ //! A simple foolproof C-string cache. +//! +//! It's easy to create dangling pointers when creating C strings. The +//! `SafeCStr` trait offers a simple conversion method while not allowing +//! dangling pointers. It uses a thread-local cache to just store the referents +//! forever. The wasted memory will likely never matter for this compiler, and +//! is easy to refactor into something more robust if it ever does matter. use std::cell::RefCell; use std::collections::HashMap; diff --git a/language/tools/move-mv-llvm-compiler/src/main.rs b/language/tools/move-mv-llvm-compiler/src/main.rs index fcd7c198f6..e365475562 100644 --- a/language/tools/move-mv-llvm-compiler/src/main.rs +++ b/language/tools/move-mv-llvm-compiler/src/main.rs @@ -115,7 +115,6 @@ fn main() -> anyhow::Result<()> { } let model_env = { - // fixme redundant bytecode deserialization let move_module = if args.is_script { let script = CompiledScript::deserialize(&bytecode_bytes) .context("Script blob can't be deserialized")?; @@ -130,15 +129,13 @@ fn main() -> anyhow::Result<()> { )? }; - /* - let llvm_context = unsafe { LLVMContextCreate() }; + // let llvm_context = unsafe { LLVMContextCreate() }; - let move_module = model_env.get_modules().next().expect("module"); - let mut disassembler = Disassembler::new(source_mapping, move_module, llvm_context); - let module = disassembler.disassemble() - .context("Failed to disassemble bytecode")?; - disassembler.llvm_write_to_file(module, args.llvm_ir, &args.output_file_path)?; - */ + // let move_module = model_env.get_modules().next().expect("module"); + // let mut disassembler = Disassembler::new(source_mapping, move_module, llvm_context); + // let module = disassembler.disassemble() + // .context("Failed to disassemble bytecode")?; + // disassembler.llvm_write_to_file(module, args.llvm_ir, &args.output_file_path)?; if args.llvm_ir && args.obj { anyhow::bail!("can't output both LLVM IR (-S) and object file (-O)"); diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/extensions.rs b/language/tools/move-mv-llvm-compiler/src/stackless/extensions.rs new file mode 100644 index 0000000000..8df6f45ad2 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/src/stackless/extensions.rs @@ -0,0 +1,26 @@ +//! Extension traits for foreign types. + +use move_model::model as mm; +use extension_trait::extension_trait; + +#[extension_trait] +pub impl<'a> ModuleEnvExt for mm::ModuleEnv<'a> { + fn llvm_module_name(&self) -> String { + self.get_full_name_str().replace(':', "_") + } +} + +#[extension_trait] +pub impl<'a> FunctionEnxExt for mm::FunctionEnv<'a> { + fn llvm_symbol_name(&self) -> String { + // fixme use get_full_name_str + let name = self.get_name_str(); + if name == "" { + // fixme move-model names script fns "". + // we might want to preserve the actual names + "main".to_string() + } else { + name + } + } +} diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs b/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs index 92da064cb6..6de211da55 100644 --- a/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs +++ b/language/tools/move-mv-llvm-compiler/src/stackless/llvm.rs @@ -174,8 +174,8 @@ impl Builder { } } - pub fn build_alloca(&self, ty: Type, name: Name) -> Alloca { - unsafe { Alloca(LLVMBuildAlloca(self.0, ty.0, name.0.cstr())) } + pub fn build_alloca(&self, ty: Type, name: &str) -> Alloca { + unsafe { Alloca(LLVMBuildAlloca(self.0, ty.0, name.cstr())) } } pub fn store_param_to_alloca(&self, param: Parameter, alloca: Alloca) { @@ -186,8 +186,7 @@ impl Builder { pub fn load_store(&self, ty: Type, src: Alloca, dst: Alloca) { unsafe { - let tmp_name = Name::kind("load_store_tmp"); - let tmp_reg = LLVMBuildLoad2(self.0, ty.0, src.0, tmp_name.0.cstr()); + let tmp_reg = LLVMBuildLoad2(self.0, ty.0, src.0, "load_store_tmp".cstr()); LLVMBuildStore(self.0, tmp_reg, dst.0); } } @@ -200,8 +199,7 @@ impl Builder { pub fn load_return(&self, ty: Type, val: Alloca) { unsafe { - let tmp_name = Name::kind("retval"); - let tmp_reg = LLVMBuildLoad2(self.0, ty.0, val.0, tmp_name.0.cstr()); + let tmp_reg = LLVMBuildLoad2(self.0, ty.0, val.0, "retval".cstr()); LLVMBuildRet(self.0, tmp_reg); } } @@ -220,8 +218,7 @@ impl Builder { pub fn load_cond_br(&self, ty: Type, val: Alloca, bb0: BasicBlock, bb1: BasicBlock) { unsafe { - let cnd_name = Name::kind("cnd"); - let cnd_reg = LLVMBuildLoad2(self.0, ty.0, val.0, cnd_name.0.cstr()); + let cnd_reg = LLVMBuildLoad2(self.0, ty.0, val.0, "cnd".cstr()); LLVMBuildCondBr(self.0, cnd_reg, bb0.0, bb1.0); } } @@ -232,8 +229,8 @@ impl Builder { .iter() .enumerate() .map(|(i, (ty, val))| { - let name = Name::new("call_arg", i); - LLVMBuildLoad2(self.0, ty.0, val.0, name.0.cstr()) + let name = format!("call_arg_{i}"); + LLVMBuildLoad2(self.0, ty.0, val.0, name.cstr()) }) .collect::>(); LLVMBuildCall2( @@ -255,12 +252,9 @@ impl Builder { pub fn load_add_store(&self, ty: Type, src0: Alloca, src1: Alloca, dst: Alloca) { unsafe { - let src0_name = Name::new("add_src", 0); - let src1_name = Name::new("add_src", 1); - let dst_name = Name::kind("add_dst"); - let src0_reg = LLVMBuildLoad2(self.0, ty.0, src0.0, src0_name.0.cstr()); - let src1_reg = LLVMBuildLoad2(self.0, ty.0, src1.0, src1_name.0.cstr()); - let dst_reg = LLVMBuildAdd(self.0, src0_reg, src1_reg, dst_name.0.cstr()); + let src0_reg = LLVMBuildLoad2(self.0, ty.0, src0.0, "add_src_0".cstr()); + let src1_reg = LLVMBuildLoad2(self.0, ty.0, src1.0, "add_src_1".cstr()); + let dst_reg = LLVMBuildAdd(self.0, src0_reg, src1_reg, "add_dst".cstr()); LLVMBuildStore(self.0, dst_reg, dst.0); } } @@ -274,12 +268,9 @@ impl Builder { pred: LLVMIntPredicate, ) { unsafe { - let src0_name = Name::new("icmp_src", 0); - let src1_name = Name::new("icmp_src", 1); - let dst_name = Name::kind("icmp_dst"); - let src0_reg = LLVMBuildLoad2(self.0, ty.0, src0.0, src0_name.0.cstr()); - let src1_reg = LLVMBuildLoad2(self.0, ty.0, src1.0, src1_name.0.cstr()); - let dst_reg = LLVMBuildICmp(self.0, pred, src0_reg, src1_reg, dst_name.0.cstr()); + let src0_reg = LLVMBuildLoad2(self.0, ty.0, src0.0, "icmp_src_0".cstr()); + let src1_reg = LLVMBuildLoad2(self.0, ty.0, src1.0, "icmp_src_1".cstr()); + let dst_reg = LLVMBuildICmp(self.0, pred, src0_reg, src1_reg, "icmp_dst".cstr()); LLVMBuildStore(self.0, dst_reg, dst.0); } } @@ -310,11 +301,8 @@ impl FunctionType { pub struct Function(LLVMValueRef); impl Function { - pub fn append_basic_block(&self, name: N) -> BasicBlock - where - N: Into, - { - unsafe { BasicBlock(LLVMAppendBasicBlock(self.0, name.into().0.cstr())) } + pub fn append_basic_block(&self, name: &str) -> BasicBlock { + unsafe { BasicBlock(LLVMAppendBasicBlock(self.0, name.cstr())) } } pub fn get_param(&self, i: usize) -> Parameter { @@ -338,31 +326,6 @@ pub struct BasicBlock(LLVMBasicBlockRef); impl BasicBlock {} -/// A local identifier, variable, block, etc. -/// -/// The trailing underscore is to visually distinguish cases where llvm renames -/// them with an appended number. -pub struct Name(String); - -impl Name { - pub fn new(kind: &str, ident: S) -> Name - where - S: std::fmt::Display, - { - Name(format!("{kind}_{ident}_")) - } - - pub fn kind(kind: &str) -> Name { - Name(format!("{kind}_")) - } -} - -impl<'a> Into for &'a str { - fn into(self) -> Name { - Name::kind(self) - } -} - #[derive(Copy, Clone)] pub struct Alloca(LLVMValueRef); diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs b/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs index aee14e2507..50812f494f 100644 --- a/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs +++ b/language/tools/move-mv-llvm-compiler/src/stackless/mod.rs @@ -1,477 +1,5 @@ -use self::exts::*; -use move_model::ast as mast; -use move_model::model as mm; -use move_model::ty as mty; -use move_stackless_bytecode::stackless_bytecode as sbc; -use std::collections::HashMap; - +mod extensions; mod llvm; +mod translate; -#[derive(Copy, Clone)] -pub enum Target { - Solana, -} - -impl Target { - fn triple(&self) -> &'static str { - match self { - Target::Solana => "bpfel-unknown-unknown", - } - } - - fn llvm_cpu(&self) -> &'static str { - match self { - Target::Solana => "generic", - } - } - - fn llvm_features(&self) -> &'static str { - match self { - Target::Solana => "+solana", - } - } - - fn initialize_llvm(&self) { - match self { - Target::Solana => { - llvm::initialize_bpf(); - } - } - } -} - -pub struct GlobalContext<'a> { - env: &'a mm::GlobalEnv, - llvm_cx: llvm::Context, - target: Target, -} - -impl<'a> GlobalContext<'a> { - pub fn new(env: &mm::GlobalEnv, target: Target) -> GlobalContext { - target.initialize_llvm(); - - GlobalContext { - env, - llvm_cx: llvm::Context::new(), - target, - } - } - - pub fn create_module_context(&self, id: mm::ModuleId) -> ModuleContext { - let env = self.env.get_module(id); - let name = env.llvm_module_name(); - ModuleContext { - env, - llvm_cx: &self.llvm_cx, - llvm_module: self.llvm_cx.create_module(&name), - llvm_builder: self.llvm_cx.create_builder(), - _target: self.target, - } - } -} - -pub struct ModuleContext<'a> { - env: mm::ModuleEnv<'a>, - llvm_cx: &'a llvm::Context, - llvm_module: llvm::Module, - llvm_builder: llvm::Builder, - _target: Target, -} - -impl<'a> ModuleContext<'a> { - pub fn translate(self) -> llvm::Module { - let filename = self.env.get_source_path().to_str().expect("utf-8"); - self.llvm_module.set_source_file_name(filename); - - for fn_env in self.env.get_functions() { - let fn_cx = self.create_fn_context(fn_env); - fn_cx.translate(); - } - - self.llvm_module.verify(); - - self.llvm_module - } - - fn create_fn_context<'b>(&'b self, fn_env: mm::FunctionEnv<'b>) -> FunctionContext<'b> { - let locals = Vec::with_capacity(fn_env.get_local_count()); - FunctionContext { - env: fn_env, - llvm_cx: &self.llvm_cx, - llvm_module: &self.llvm_module, - llvm_builder: &self.llvm_builder, - label_blocks: HashMap::new(), - locals, - } - } -} - -struct FunctionContext<'a> { - env: mm::FunctionEnv<'a>, - llvm_cx: &'a llvm::Context, - llvm_module: &'a llvm::Module, - llvm_builder: &'a llvm::Builder, - label_blocks: HashMap, - /// Corresponds to FunctionData:local_types - locals: Vec, -} - -/// A stackless move local variable, translated as an llvm alloca -struct Local { - mty: mty::Type, - llty: llvm::Type, - llval: llvm::Alloca, -} - -impl<'a> FunctionContext<'a> { - fn translate(mut self) { - use move_stackless_bytecode::stackless_bytecode_generator::StacklessBytecodeGenerator; - - let fn_data = StacklessBytecodeGenerator::new(&self.env).generate_function(); - - dbg!(&fn_data); - - // Create the llvm function - let ll_fn = { - let ll_fnty = { - let ll_rty = match fn_data.return_types.len() { - 0 => self.llvm_cx.void_type(), - 1 => self.llvm_type(&fn_data.return_types[0]), - _ => { - todo!() - } - }; - - let ll_parm_tys = self - .env - .get_parameter_types() - .iter() - .map(|mty| self.llvm_type(mty)) - .collect::>(); - - llvm::FunctionType::new(ll_rty, &ll_parm_tys) - }; - - self.llvm_module - .add_function(&self.env.llvm_symbol_name(), ll_fnty) - }; - - // Create basic blocks and position builder at entry block - { - let entry_block = ll_fn.append_basic_block("entry"); - - // Create basic blocks for move labels - for instr in &fn_data.code { - match instr { - sbc::Bytecode::Label(_, label) => { - let name = llvm::Name::new("bb", label.as_usize()); - let llbb = ll_fn.append_basic_block(name); - self.label_blocks.insert(*label, llbb); - } - _ => {} - } - } - - self.llvm_builder.position_at_end(entry_block); - } - - // Declare all the locals as allocas - { - for (i, mty) in fn_data.local_types.iter().enumerate() { - let llty = self.llvm_type(mty); - let name = llvm::Name::new("local", i); - let llval = self.llvm_builder.build_alloca(llty, name); - self.locals.push(Local { - mty: mty.clone(), // fixme bad clone - llty, - llval, - }); - } - } - - // Store params into locals - { - let param_count = self.env.get_parameter_count(); - let ll_params = (0..param_count).map(|i| ll_fn.get_param(i)); - - for (ll_param, local) in ll_params.zip(self.locals.iter()) { - self.llvm_builder - .store_param_to_alloca(ll_param, local.llval); - } - } - - // Translate instructions - for instr in &fn_data.code { - self.translate_instruction(instr); - } - - ll_fn.verify(); - } - - fn translate_instruction(&self, instr: &sbc::Bytecode) { - match instr { - sbc::Bytecode::Assign(_, dst, src, sbc::AssignKind::Move) => { - let mty = &self.locals[*dst].mty; - let llty = self.locals[*dst].llty; - let dst_llval = self.locals[*dst].llval; - let src_llval = self.locals[*src].llval; - match mty { - mty::Type::Primitive(mty::PrimitiveType::Bool | mty::PrimitiveType::U8) => { - self.llvm_builder.load_store(llty, src_llval, dst_llval); - } - _ => todo!(), - } - } - sbc::Bytecode::Assign(_, dst, src, sbc::AssignKind::Store) => { - let mty = &self.locals[*dst].mty; - let llty = self.locals[*dst].llty; - let dst_llval = self.locals[*dst].llval; - let src_llval = self.locals[*src].llval; - match mty { - mty::Type::Primitive(mty::PrimitiveType::Bool | mty::PrimitiveType::U8) => { - self.llvm_builder.load_store(llty, src_llval, dst_llval); - } - _ => todo!(), - } - } - sbc::Bytecode::Call(_, dst, op, src, None) => { - self.translate_call(dst, op, src); - } - sbc::Bytecode::Ret(_, vals) => match vals.len() { - 0 => { - self.llvm_builder.build_return_void(); - } - 1 => { - let idx = vals[0]; - let llval = self.locals[idx].llval; - let llty = self.locals[idx].llty; - self.llvm_builder.load_return(llty, llval); - } - _ => todo!(), - }, - sbc::Bytecode::Load(_, idx, val) => { - let local_llval = self.locals[*idx].llval; - let const_llval = self.constant(val); - self.llvm_builder.store_const(const_llval, local_llval); - } - sbc::Bytecode::Branch(_, label0, label1, cnd_idx) => { - let cnd_llval = self.locals[*cnd_idx].llval; - let cnd_llty = self.locals[*cnd_idx].llty; - let bb0 = self.label_blocks[label0]; - let bb1 = self.label_blocks[label1]; - self.llvm_builder - .load_cond_br(cnd_llty, cnd_llval, bb0, bb1); - } - sbc::Bytecode::Jump(_, label) => { - let llbb = self.label_blocks[label]; - self.llvm_builder.build_br(llbb); - } - sbc::Bytecode::Label(_, label) => { - let llbb = self.label_blocks[label]; - self.llvm_builder.position_at_end(llbb); - } - sbc::Bytecode::Abort(_, local) => { - self.emit_rtcall(RtCall::Abort(*local)); - } - _ => { - todo!() - } - } - } - - fn translate_call( - &self, - dst: &[mast::TempIndex], - op: &sbc::Operation, - src: &[mast::TempIndex], - ) { - use sbc::Operation; - match op { - Operation::Destroy => { - assert!(dst.is_empty()); - assert_eq!(src.len(), 1); - let idx = src[0]; - let mty = &self.locals[idx].mty; - match mty { - mty::Type::Primitive(_) => ( /* nop */ ), - _ => todo!(), - } - } - Operation::Add => { - assert_eq!(dst.len(), 1); - assert_eq!(src.len(), 2); - let dst_idx = dst[0]; - let src0_idx = src[0]; - let src1_idx = src[1]; - let dst_llval = self.locals[dst_idx].llval; - let src0_llval = self.locals[src0_idx].llval; - let src1_llval = self.locals[src1_idx].llval; - let mty = &self.locals[src0_idx].mty; - let llty = self.locals[src0_idx].llty; - match mty { - mty::Type::Primitive(mty::PrimitiveType::U8) => { - self.llvm_builder - .load_add_store(llty, src0_llval, src1_llval, dst_llval); - } - _ => todo!(), - } - } - Operation::Sub => todo!(), - Operation::Mul => todo!(), - Operation::Div => todo!(), - Operation::Mod => todo!(), - Operation::BitOr => todo!(), - Operation::BitAnd => todo!(), - Operation::Xor => todo!(), - Operation::Shl => todo!(), - Operation::Shr => todo!(), - Operation::Eq => { - assert_eq!(dst.len(), 1); - assert_eq!(src.len(), 2); - let dst_idx = dst[0]; - let src0_idx = src[0]; - let src1_idx = src[1]; - let dst_llval = self.locals[dst_idx].llval; - let src0_llval = self.locals[src0_idx].llval; - let src1_llval = self.locals[src1_idx].llval; - let mty = &self.locals[src0_idx].mty; - let llty = self.locals[src0_idx].llty; - match mty { - mty::Type::Primitive(mty::PrimitiveType::U8) => { - self.llvm_builder.load_icmp_store( - llty, - src0_llval, - src1_llval, - dst_llval, - llvm::LLVMIntPredicate::LLVMIntEQ, - ); - } - _ => todo!(), - } - } - _ => todo!(), - } - } - - fn llvm_type(&self, mty: &mty::Type) -> llvm::Type { - use mty::{PrimitiveType, Type}; - - match mty { - Type::Primitive(PrimitiveType::Bool) => self.llvm_cx.int1_type(), - Type::Primitive(PrimitiveType::U8) => self.llvm_cx.int8_type(), - Type::Primitive(PrimitiveType::U64) => self.llvm_cx.int64_type(), - _ => { - todo!() - } - } - } - - fn constant(&self, mc: &sbc::Constant) -> llvm::Constant { - use sbc::Constant; - match mc { - Constant::U8(val) => { - let llty = self.llvm_cx.int8_type(); - llvm::Constant::int(llty, *val as u64) - } - Constant::U64(val) => { - let llty = self.llvm_cx.int64_type(); - llvm::Constant::int(llty, *val) - } - _ => todo!(), - } - } - - fn emit_rtcall(&self, rtcall: RtCall) { - match &rtcall { - RtCall::Abort(local_idx) => { - let (llfn, llfnty) = self.get_runtime_function(&rtcall); - let local_llval = self.locals[*local_idx].llval; - let local_llty = self.locals[*local_idx].llty; - self.llvm_builder - .load_call(llfnty, llfn, &[(local_llty, local_llval)]); - self.llvm_builder.build_unreachable(); - } - } - } - - fn get_runtime_function(&self, rtcall: &RtCall) -> (llvm::Function, llvm::FunctionType) { - let name = match rtcall { - RtCall::Abort(..) => "abort", - }; - let name = format!("move_rt_{name}"); - let llfn = self.llvm_module.get_named_function(&name); - if let Some(llfn) = llfn { - let llty = llfn.llvm_type(); - (llfn, llty) - } else { - let (llty, attrs) = match rtcall { - RtCall::Abort(..) => { - let ret_ty = self.llvm_cx.void_type(); - let param_tys = &[self.llvm_cx.int64_type()]; - let llty = llvm::FunctionType::new(ret_ty, param_tys); - let attrs = vec![llvm::AttributeKind::NoReturn]; - (llty, attrs) - } - }; - - let llfn = self - .llvm_module - .add_function_with_attrs(&name, llty, &attrs); - (llfn, llty) - } - } -} - -mod exts { - use super::mm; - use extension_trait::extension_trait; - - #[extension_trait] - pub impl<'a> ModuleEnvExt for mm::ModuleEnv<'a> { - fn llvm_module_name(&self) -> String { - self.get_full_name_str().replace(':', "_") - } - } - - #[extension_trait] - pub impl<'a> FunctionEnxExt for mm::FunctionEnv<'a> { - fn llvm_symbol_name(&self) -> String { - // fixme use get_full_name_str - let name = self.get_name_str(); - if name == "" { - // fixme move-model names script fns "". - // we might want to preserve the actual names - "main".to_string() - } else { - name - } - } - } -} - -pub enum RtCall { - Abort(mast::TempIndex), -} - - -/// Compile the module to object file. -/// -/// This takes the module by value because it would otherwise have -/// side effects, mutating target-specific properties. -pub fn write_object_file(llmod: llvm::Module, target: Target, outpath: &str) -> anyhow::Result<()> { - let lltarget = llvm::Target::from_triple(target.triple())?; - let llmachine = lltarget.create_target_machine( - target.triple(), - target.llvm_cpu(), - target.llvm_features(), - ); - - llmod.set_target(target.triple()); - llmod.set_data_layout(&llmachine); - - llmod.verify(); - - llmachine.emit_to_obj_file(&llmod, outpath)?; - - Ok(()) -} +pub use translate::*; diff --git a/language/tools/move-mv-llvm-compiler/src/stackless/translate.rs b/language/tools/move-mv-llvm-compiler/src/stackless/translate.rs new file mode 100644 index 0000000000..1b6853419b --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/src/stackless/translate.rs @@ -0,0 +1,481 @@ +//! Translation from stackless Move bytecode to LLVM. +//! +//! Move is a stack machine and challenging to translate directly to LLVM. The +//! `move_model` crate provides a translation of Move bytecode to "stackless +//! bytecode", which is well-suited to further translation to LLVM. +//! +//! The structure of this module naturally mirrors both the Move model and LLVM +//! sys, with a `GlobalContext` holding the Move `GlobalEnv` and the LLVM +//! `Context`. Modules are translated through a `ModuleContext`, and functions a +//! `FunctionContext`, each of which may accessed cached information from the +//! parent context, all linked through lifetimes. +//! +//! +//! # Lifetimes +//! +//! This module attempts to keep distinct lifetimes distinct to avoid +//! a situation where they have be disentangled later. The structures +//! contain two named lifetimes: +//! +//! - `'mm` - the lifetime of types stored inside the `move_model` `GlobalEnv` +//! - `'up` - reference up the callstack to the higher-level context struct +//! +//! When constructing a new context the local lifetime that becomes `'up` +//! is named `'this`. +//! +//! In general though this compiler does not need to be efficient at compile time - +//! we can clone things when it makes managing lifetimes easier. + +use move_model::ast as mast; +use move_model::model as mm; +use move_model::ty as mty; +use move_stackless_bytecode::stackless_bytecode as sbc; +use std::collections::HashMap; +use crate::stackless::extensions::*; +use crate::stackless::llvm; + +#[derive(Copy, Clone)] +pub enum Target { + Solana, +} + +impl Target { + fn triple(&self) -> &'static str { + match self { + Target::Solana => "bpfel-unknown-unknown", + } + } + + fn llvm_cpu(&self) -> &'static str { + match self { + Target::Solana => "generic", + } + } + + fn llvm_features(&self) -> &'static str { + match self { + Target::Solana => "+solana", + } + } + + fn initialize_llvm(&self) { + match self { + Target::Solana => { + llvm::initialize_bpf(); + } + } + } +} + +pub struct GlobalContext<'up> { + env: &'up mm::GlobalEnv, + llvm_cx: llvm::Context, + target: Target, +} + +impl<'up> GlobalContext<'up> { + pub fn new(env: &'up mm::GlobalEnv, target: Target) -> GlobalContext { + target.initialize_llvm(); + + GlobalContext { + env, + llvm_cx: llvm::Context::new(), + target, + } + } + + pub fn create_module_context<'this>(&'this self, id: mm::ModuleId) -> ModuleContext<'up, 'this> { + let env = self.env.get_module(id); + let name = env.llvm_module_name(); + ModuleContext { + env, + llvm_cx: &self.llvm_cx, + llvm_module: self.llvm_cx.create_module(&name), + llvm_builder: self.llvm_cx.create_builder(), + _target: self.target, + } + } +} + +pub struct ModuleContext<'mm, 'up> { + env: mm::ModuleEnv<'mm>, + llvm_cx: &'up llvm::Context, + llvm_module: llvm::Module, + llvm_builder: llvm::Builder, + _target: Target, +} + +impl<'mm, 'up> ModuleContext<'mm, 'up> { + pub fn translate(self) -> llvm::Module { + let filename = self.env.get_source_path().to_str().expect("utf-8"); + self.llvm_module.set_source_file_name(filename); + + // todo declare all functions before translating them + // and cache their llvm values for use by callsites + + for fn_env in self.env.get_functions() { + let fn_cx = self.create_fn_context(fn_env); + fn_cx.translate(); + } + + self.llvm_module.verify(); + + self.llvm_module + } + + fn create_fn_context<'this>(&'this self, fn_env: mm::FunctionEnv<'mm>) -> FunctionContext<'mm, 'this> { + let locals = Vec::with_capacity(fn_env.get_local_count()); + FunctionContext { + env: fn_env, + llvm_cx: &self.llvm_cx, + llvm_module: &self.llvm_module, + llvm_builder: &self.llvm_builder, + label_blocks: HashMap::new(), + locals, + } + } +} + +struct FunctionContext<'mm, 'up> { + env: mm::FunctionEnv<'mm>, + llvm_cx: &'up llvm::Context, + llvm_module: &'up llvm::Module, + llvm_builder: &'up llvm::Builder, + label_blocks: HashMap, + /// Corresponds to FunctionData:local_types + locals: Vec, +} + +/// A stackless move local variable, translated as an llvm alloca +struct Local { + mty: mty::Type, + llty: llvm::Type, + llval: llvm::Alloca, +} + +impl<'mm, 'up> FunctionContext<'mm, 'up> { + fn translate(mut self) { + use move_stackless_bytecode::stackless_bytecode_generator::StacklessBytecodeGenerator; + + let fn_data = StacklessBytecodeGenerator::new(&self.env).generate_function(); + + dbg!(&fn_data); + + // Create the llvm function + // todo do this in ModuleContext::translate + let ll_fn = { + let ll_fnty = { + let ll_rty = match fn_data.return_types.len() { + 0 => self.llvm_cx.void_type(), + 1 => self.llvm_type(&fn_data.return_types[0]), + _ => { + todo!() + } + }; + + let ll_parm_tys = self + .env + .get_parameter_types() + .iter() + .map(|mty| self.llvm_type(mty)) + .collect::>(); + + llvm::FunctionType::new(ll_rty, &ll_parm_tys) + }; + + self.llvm_module + .add_function(&self.env.llvm_symbol_name(), ll_fnty) + }; + + // Create basic blocks and position builder at entry block + { + let entry_block = ll_fn.append_basic_block("entry"); + + // Create basic blocks for move labels + for instr in &fn_data.code { + match instr { + sbc::Bytecode::Label(_, label) => { + let name = format!("bb_{}", label.as_usize()); + let llbb = ll_fn.append_basic_block(&name); + self.label_blocks.insert(*label, llbb); + } + _ => {} + } + } + + self.llvm_builder.position_at_end(entry_block); + } + + // Declare all the locals as allocas + { + for (i, mty) in fn_data.local_types.iter().enumerate() { + let llty = self.llvm_type(mty); + let name = format!("local_{}", i); + let llval = self.llvm_builder.build_alloca(llty, &name); + self.locals.push(Local { + mty: mty.clone(), // fixme bad clone + llty, + llval, + }); + } + } + + // Store params into locals + { + let param_count = self.env.get_parameter_count(); + let ll_params = (0..param_count).map(|i| ll_fn.get_param(i)); + + for (ll_param, local) in ll_params.zip(self.locals.iter()) { + self.llvm_builder + .store_param_to_alloca(ll_param, local.llval); + } + } + + // Translate instructions + for instr in &fn_data.code { + self.translate_instruction(instr); + } + + ll_fn.verify(); + } + + fn translate_instruction(&self, instr: &sbc::Bytecode) { + match instr { + sbc::Bytecode::Assign(_, dst, src, sbc::AssignKind::Move) => { + let mty = &self.locals[*dst].mty; + let llty = self.locals[*dst].llty; + let dst_llval = self.locals[*dst].llval; + let src_llval = self.locals[*src].llval; + match mty { + mty::Type::Primitive(mty::PrimitiveType::Bool | mty::PrimitiveType::U8) => { + self.llvm_builder.load_store(llty, src_llval, dst_llval); + } + _ => todo!(), + } + } + sbc::Bytecode::Assign(_, dst, src, sbc::AssignKind::Store) => { + let mty = &self.locals[*dst].mty; + let llty = self.locals[*dst].llty; + let dst_llval = self.locals[*dst].llval; + let src_llval = self.locals[*src].llval; + match mty { + mty::Type::Primitive(mty::PrimitiveType::Bool | mty::PrimitiveType::U8) => { + self.llvm_builder.load_store(llty, src_llval, dst_llval); + } + _ => todo!(), + } + } + sbc::Bytecode::Call(_, dst, op, src, None) => { + self.translate_call(dst, op, src); + } + sbc::Bytecode::Ret(_, vals) => match vals.len() { + 0 => { + self.llvm_builder.build_return_void(); + } + 1 => { + let idx = vals[0]; + let llval = self.locals[idx].llval; + let llty = self.locals[idx].llty; + self.llvm_builder.load_return(llty, llval); + } + _ => todo!(), + }, + sbc::Bytecode::Load(_, idx, val) => { + let local_llval = self.locals[*idx].llval; + let const_llval = self.constant(val); + self.llvm_builder.store_const(const_llval, local_llval); + } + sbc::Bytecode::Branch(_, label0, label1, cnd_idx) => { + let cnd_llval = self.locals[*cnd_idx].llval; + let cnd_llty = self.locals[*cnd_idx].llty; + let bb0 = self.label_blocks[label0]; + let bb1 = self.label_blocks[label1]; + self.llvm_builder + .load_cond_br(cnd_llty, cnd_llval, bb0, bb1); + } + sbc::Bytecode::Jump(_, label) => { + let llbb = self.label_blocks[label]; + self.llvm_builder.build_br(llbb); + } + sbc::Bytecode::Label(_, label) => { + let llbb = self.label_blocks[label]; + self.llvm_builder.position_at_end(llbb); + } + sbc::Bytecode::Abort(_, local) => { + self.emit_rtcall(RtCall::Abort(*local)); + } + _ => { + todo!() + } + } + } + + fn translate_call( + &self, + dst: &[mast::TempIndex], + op: &sbc::Operation, + src: &[mast::TempIndex], + ) { + use sbc::Operation; + match op { + Operation::Destroy => { + assert!(dst.is_empty()); + assert_eq!(src.len(), 1); + let idx = src[0]; + let mty = &self.locals[idx].mty; + match mty { + mty::Type::Primitive(_) => ( /* nop */ ), + _ => todo!(), + } + } + Operation::Add => { + assert_eq!(dst.len(), 1); + assert_eq!(src.len(), 2); + let dst_idx = dst[0]; + let src0_idx = src[0]; + let src1_idx = src[1]; + let dst_llval = self.locals[dst_idx].llval; + let src0_llval = self.locals[src0_idx].llval; + let src1_llval = self.locals[src1_idx].llval; + let mty = &self.locals[src0_idx].mty; + let llty = self.locals[src0_idx].llty; + match mty { + mty::Type::Primitive(mty::PrimitiveType::U8) => { + self.llvm_builder + .load_add_store(llty, src0_llval, src1_llval, dst_llval); + } + _ => todo!(), + } + } + Operation::Sub => todo!(), + Operation::Mul => todo!(), + Operation::Div => todo!(), + Operation::Mod => todo!(), + Operation::BitOr => todo!(), + Operation::BitAnd => todo!(), + Operation::Xor => todo!(), + Operation::Shl => todo!(), + Operation::Shr => todo!(), + Operation::Eq => { + assert_eq!(dst.len(), 1); + assert_eq!(src.len(), 2); + let dst_idx = dst[0]; + let src0_idx = src[0]; + let src1_idx = src[1]; + let dst_llval = self.locals[dst_idx].llval; + let src0_llval = self.locals[src0_idx].llval; + let src1_llval = self.locals[src1_idx].llval; + let mty = &self.locals[src0_idx].mty; + let llty = self.locals[src0_idx].llty; + match mty { + mty::Type::Primitive(mty::PrimitiveType::U8) => { + self.llvm_builder.load_icmp_store( + llty, + src0_llval, + src1_llval, + dst_llval, + llvm::LLVMIntPredicate::LLVMIntEQ, + ); + } + _ => todo!(), + } + } + _ => todo!(), + } + } + + fn llvm_type(&self, mty: &mty::Type) -> llvm::Type { + use mty::{PrimitiveType, Type}; + + match mty { + Type::Primitive(PrimitiveType::Bool) => self.llvm_cx.int1_type(), + Type::Primitive(PrimitiveType::U8) => self.llvm_cx.int8_type(), + Type::Primitive(PrimitiveType::U64) => self.llvm_cx.int64_type(), + _ => { + todo!() + } + } + } + + fn constant(&self, mc: &sbc::Constant) -> llvm::Constant { + use sbc::Constant; + match mc { + Constant::U8(val) => { + let llty = self.llvm_cx.int8_type(); + llvm::Constant::int(llty, *val as u64) + } + Constant::U64(val) => { + let llty = self.llvm_cx.int64_type(); + llvm::Constant::int(llty, *val) + } + _ => todo!(), + } + } + + fn emit_rtcall(&self, rtcall: RtCall) { + match &rtcall { + RtCall::Abort(local_idx) => { + let (llfn, llfnty) = self.get_runtime_function(&rtcall); + let local_llval = self.locals[*local_idx].llval; + let local_llty = self.locals[*local_idx].llty; + self.llvm_builder + .load_call(llfnty, llfn, &[(local_llty, local_llval)]); + self.llvm_builder.build_unreachable(); + } + } + } + + fn get_runtime_function(&self, rtcall: &RtCall) -> (llvm::Function, llvm::FunctionType) { + let name = match rtcall { + RtCall::Abort(..) => "abort", + }; + let name = format!("move_rt_{name}"); + let llfn = self.llvm_module.get_named_function(&name); + if let Some(llfn) = llfn { + let llty = llfn.llvm_type(); + (llfn, llty) + } else { + let (llty, attrs) = match rtcall { + RtCall::Abort(..) => { + let ret_ty = self.llvm_cx.void_type(); + let param_tys = &[self.llvm_cx.int64_type()]; + let llty = llvm::FunctionType::new(ret_ty, param_tys); + let attrs = vec![llvm::AttributeKind::NoReturn]; + (llty, attrs) + } + }; + + let llfn = self + .llvm_module + .add_function_with_attrs(&name, llty, &attrs); + (llfn, llty) + } + } +} + +pub enum RtCall { + Abort(mast::TempIndex), +} + + +/// Compile the module to object file. +/// +/// This takes the module by value because it would otherwise have +/// side effects, mutating target-specific properties. +pub fn write_object_file(llmod: llvm::Module, target: Target, outpath: &str) -> anyhow::Result<()> { + let lltarget = llvm::Target::from_triple(target.triple())?; + let llmachine = lltarget.create_target_machine( + target.triple(), + target.llvm_cpu(), + target.llvm_features(), + ); + + llmod.set_target(target.triple()); + llmod.set_data_layout(&llmachine); + + llmod.verify(); + + llmachine.emit_to_obj_file(&llmod, outpath)?; + + Ok(()) +} diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll index 4d4e79a967..2c625a21b5 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/abort-build/modules/0_Test.expected.ll @@ -2,11 +2,11 @@ source_filename = "" define void @test() { -entry_: - %local_0_ = alloca i64, align 8 - store i64 10, ptr %local_0_, align 4 - %call_arg_0_ = load i64, ptr %local_0_, align 4 - call void @move_rt_abort(i64 %call_arg_0_) +entry: + %local_0 = alloca i64, align 8 + store i64 10, ptr %local_0, align 4 + %call_arg_0 = load i64, ptr %local_0, align 4 + call void @move_rt_abort(i64 %call_arg_0) unreachable } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll index 4d4e79a967..2c625a21b5 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assert-build/modules/0_Test.expected.ll @@ -2,11 +2,11 @@ source_filename = "" define void @test() { -entry_: - %local_0_ = alloca i64, align 8 - store i64 10, ptr %local_0_, align 4 - %call_arg_0_ = load i64, ptr %local_0_, align 4 - call void @move_rt_abort(i64 %call_arg_0_) +entry: + %local_0 = alloca i64, align 8 + store i64 10, ptr %local_0, align 4 + %call_arg_0 = load i64, ptr %local_0, align 4 + call void @move_rt_abort(i64 %call_arg_0) unreachable } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll index fd244a9bd7..772cdd5e1c 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/assign-build/scripts/main.expected.ll @@ -2,8 +2,8 @@ source_filename = "" define void @main() { -entry_: - %local_0_ = alloca i8, align 1 - store i8 7, ptr %local_0_, align 1 +entry: + %local_0 = alloca i8, align 1 + store i8 7, ptr %local_0, align 1 ret void } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll index 59c4802bea..02ca8addda 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/eq-build/modules/0_Test.expected.ll @@ -2,22 +2,22 @@ source_filename = "" define i1 @test(i8 %0, i8 %1) { -entry_: - %local_0_ = alloca i8, align 1 - %local_1_ = alloca i8, align 1 - %local_2_ = alloca i8, align 1 - %local_3_ = alloca i8, align 1 - %local_4_ = alloca i1, align 1 - store i8 %0, ptr %local_0_, align 1 - store i8 %1, ptr %local_1_, align 1 - %load_store_tmp_ = load i8, ptr %local_0_, align 1 - store i8 %load_store_tmp_, ptr %local_2_, align 1 - %load_store_tmp_1 = load i8, ptr %local_1_, align 1 - store i8 %load_store_tmp_1, ptr %local_3_, align 1 - %icmp_src_0_ = load i8, ptr %local_2_, align 1 - %icmp_src_1_ = load i8, ptr %local_3_, align 1 - %icmp_dst_ = icmp eq i8 %icmp_src_0_, %icmp_src_1_ - store i1 %icmp_dst_, ptr %local_4_, align 1 - %retval_ = load i1, ptr %local_4_, align 1 - ret i1 %retval_ +entry: + %local_0 = alloca i8, align 1 + %local_1 = alloca i8, align 1 + %local_2 = alloca i8, align 1 + %local_3 = alloca i8, align 1 + %local_4 = alloca i1, align 1 + store i8 %0, ptr %local_0, align 1 + store i8 %1, ptr %local_1, align 1 + %load_store_tmp = load i8, ptr %local_0, align 1 + store i8 %load_store_tmp, ptr %local_2, align 1 + %load_store_tmp1 = load i8, ptr %local_1, align 1 + store i8 %load_store_tmp1, ptr %local_3, align 1 + %icmp_src_0 = load i8, ptr %local_2, align 1 + %icmp_src_1 = load i8, ptr %local_3, align 1 + %icmp_dst = icmp eq i8 %icmp_src_0, %icmp_src_1 + store i1 %icmp_dst, ptr %local_4, align 1 + %retval = load i1, ptr %local_4, align 1 + ret i1 %retval } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll index cabfcc3fce..abdd6e36e6 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/if-build/modules/0_Test.expected.ll @@ -2,34 +2,34 @@ source_filename = "" define i8 @test(i1 %0) { -entry_: - %local_0_ = alloca i1, align 1 - %local_1_ = alloca i8, align 1 - %local_2_ = alloca i1, align 1 - %local_3_ = alloca i8, align 1 - %local_4_ = alloca i8, align 1 - %local_5_ = alloca i8, align 1 - store i1 %0, ptr %local_0_, align 1 - %load_store_tmp_ = load i1, ptr %local_0_, align 1 - store i1 %load_store_tmp_, ptr %local_2_, align 1 - %cnd_ = load i1, ptr %local_2_, align 1 - br i1 %cnd_, label %bb_1_, label %bb_0_ +entry: + %local_0 = alloca i1, align 1 + %local_1 = alloca i8, align 1 + %local_2 = alloca i1, align 1 + %local_3 = alloca i8, align 1 + %local_4 = alloca i8, align 1 + %local_5 = alloca i8, align 1 + store i1 %0, ptr %local_0, align 1 + %load_store_tmp = load i1, ptr %local_0, align 1 + store i1 %load_store_tmp, ptr %local_2, align 1 + %cnd = load i1, ptr %local_2, align 1 + br i1 %cnd, label %bb_1, label %bb_0 -bb_1_: ; preds = %entry_ - store i8 2, ptr %local_3_, align 1 - %load_store_tmp_1 = load i8, ptr %local_3_, align 1 - store i8 %load_store_tmp_1, ptr %local_1_, align 1 - br label %bb_2_ +bb_1: ; preds = %entry + store i8 2, ptr %local_3, align 1 + %load_store_tmp1 = load i8, ptr %local_3, align 1 + store i8 %load_store_tmp1, ptr %local_1, align 1 + br label %bb_2 -bb_0_: ; preds = %entry_ - store i8 3, ptr %local_4_, align 1 - %load_store_tmp_2 = load i8, ptr %local_4_, align 1 - store i8 %load_store_tmp_2, ptr %local_1_, align 1 - br label %bb_2_ +bb_0: ; preds = %entry + store i8 3, ptr %local_4, align 1 + %load_store_tmp2 = load i8, ptr %local_4, align 1 + store i8 %load_store_tmp2, ptr %local_1, align 1 + br label %bb_2 -bb_2_: ; preds = %bb_0_, %bb_1_ - %load_store_tmp_3 = load i8, ptr %local_1_, align 1 - store i8 %load_store_tmp_3, ptr %local_5_, align 1 - %retval_ = load i8, ptr %local_5_, align 1 - ret i8 %retval_ +bb_2: ; preds = %bb_0, %bb_1 + %load_store_tmp3 = load i8, ptr %local_1, align 1 + store i8 %load_store_tmp3, ptr %local_5, align 1 + %retval = load i8, ptr %local_5, align 1 + ret i8 %retval } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll index b0c4684b40..1d9f3f9313 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/math_u8-build/modules/0_Test.expected.ll @@ -2,22 +2,22 @@ source_filename = "" define i8 @test(i8 %0, i8 %1) { -entry_: - %local_0_ = alloca i8, align 1 - %local_1_ = alloca i8, align 1 - %local_2_ = alloca i8, align 1 - %local_3_ = alloca i8, align 1 - %local_4_ = alloca i8, align 1 - store i8 %0, ptr %local_0_, align 1 - store i8 %1, ptr %local_1_, align 1 - %load_store_tmp_ = load i8, ptr %local_0_, align 1 - store i8 %load_store_tmp_, ptr %local_2_, align 1 - %load_store_tmp_1 = load i8, ptr %local_1_, align 1 - store i8 %load_store_tmp_1, ptr %local_3_, align 1 - %add_src_0_ = load i8, ptr %local_2_, align 1 - %add_src_1_ = load i8, ptr %local_3_, align 1 - %add_dst_ = add i8 %add_src_0_, %add_src_1_ - store i8 %add_dst_, ptr %local_4_, align 1 - %retval_ = load i8, ptr %local_4_, align 1 - ret i8 %retval_ +entry: + %local_0 = alloca i8, align 1 + %local_1 = alloca i8, align 1 + %local_2 = alloca i8, align 1 + %local_3 = alloca i8, align 1 + %local_4 = alloca i8, align 1 + store i8 %0, ptr %local_0, align 1 + store i8 %1, ptr %local_1, align 1 + %load_store_tmp = load i8, ptr %local_0, align 1 + store i8 %load_store_tmp, ptr %local_2, align 1 + %load_store_tmp1 = load i8, ptr %local_1, align 1 + store i8 %load_store_tmp1, ptr %local_3, align 1 + %add_src_0 = load i8, ptr %local_2, align 1 + %add_src_1 = load i8, ptr %local_3, align 1 + %add_dst = add i8 %add_src_0, %add_src_1 + store i8 %add_dst, ptr %local_4, align 1 + %retval = load i8, ptr %local_4, align 1 + ret i8 %retval } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll index d971962be5..508a15867a 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/return-build/modules/0_Test.expected.ll @@ -2,9 +2,9 @@ source_filename = "" define i8 @test() { -entry_: - %local_0_ = alloca i8, align 1 - store i8 100, ptr %local_0_, align 1 - %retval_ = load i8, ptr %local_0_, align 1 - ret i8 %retval_ +entry: + %local_0 = alloca i8, align 1 + store i8 100, ptr %local_0, align 1 + %retval = load i8, ptr %local_0, align 1 + ret i8 %retval } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll index bcc4ed7b65..d105b01b08 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script-build/scripts/main.expected.ll @@ -2,6 +2,6 @@ source_filename = "" define void @main() { -entry_: +entry: ret void } diff --git a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script.move b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script.move index e037b807bb..70cf220068 100644 --- a/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script.move +++ b/language/tools/move-mv-llvm-compiler/tests/move-ir-tests/script.move @@ -1,4 +1,3 @@ -// ignore script { fun main() { } } diff --git a/language/tools/move-mv-llvm-compiler/tests/rbpf-tests.rs b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests.rs index 2772ca3393..f4711295cc 100644 --- a/language/tools/move-mv-llvm-compiler/tests/rbpf-tests.rs +++ b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests.rs @@ -92,36 +92,6 @@ fn get_bpf_tools() -> anyhow::Result { Ok(bpf_tools) } -#[allow(unused)] -fn link_object_files_(test_plan: &tc::TestPlan, bpf_tools: &BpfTools, compilation_units: &[tc::CompilationUnit]) -> anyhow::Result { - let output_dylib = test_plan.build_dir.join("output.so"); - - let mut cmd = Command::new(&bpf_tools.clang); - //cmd.arg("--target=bpfel-unknown-unknown"); - cmd.args(["-target", "bpf"]); - cmd.arg("-fPIC"); - cmd.arg("-march=bpfel+solana"); - cmd.arg(format!("-fuse-ld={}", bpf_tools.lld.display())); - cmd.arg("-shared"); // create a shared library - cmd.arg("-o"); - cmd.arg(&output_dylib); - cmd.arg("-v"); - - for cu in compilation_units { - cmd.arg(&cu.object_file()); - } - - let output = cmd.output()?; - if !output.status.success() { - anyhow::bail!( - "linking with lld failed. stderr:\n\n{}", - String::from_utf8_lossy(&output.stderr) - ); - } - - Ok(output_dylib) -} - fn link_object_files(test_plan: &tc::TestPlan, bpf_tools: &BpfTools, compilation_units: &[tc::CompilationUnit]) -> anyhow::Result { let output_dylib = test_plan.build_dir.join("output.so"); @@ -154,11 +124,9 @@ fn run_rbpf(exe: &Path) -> anyhow::Result<()> { use rbpf::elf::Executable; use rbpf::ebpf; use rbpf::verifier::RequisiteVerifier; - //use rbpf::elf_parser_glue::{GoblinParser, ElfParser}; use std::sync::Arc; let elf = &std::fs::read(exe)?; - //let parser = GoblinParser::parse(elf)?; let mem = &mut vec![0; 1024]; let config = Config { @@ -166,19 +134,25 @@ fn run_rbpf(exe: &Path) -> anyhow::Result<()> { enable_elf_vaddr: false, reject_rodata_stack_overlap: false, static_syscalls: false, - optimize_rodata: false, - new_elf_parser: true, .. Config::default() }; let loader = Arc::new(BuiltInProgram::new_loader(config)); - //let function_registry = FunctionRegistry::default(); let executable = Executable::::from_elf(elf, loader).unwrap(); let mem_region = MemoryRegion::new_writable(mem, ebpf::MM_INPUT_START); let verified_executable = VerifiedExecutable::::from_executable(executable).unwrap(); let mut context_object = TestContextObject::new(1); let mut vm = EbpfVm::new(&verified_executable, &mut context_object, &mut [], vec![mem_region]).unwrap(); - let (_instruction_count, _result) = vm.execute_program(true); + let (_instruction_count, result) = vm.execute_program(true); + + let result = Result::from(result); + + match result { + Ok(0) => { } + e => { + panic!("{e:?}"); + } + } Ok(()) } diff --git a/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/script-build/scripts/main.mv b/language/tools/move-mv-llvm-compiler/tests/rbpf-tests/script-build/scripts/main.mv deleted file mode 100644 index a76c05de9aca86feb46ad9a540fd6ac334f9bc06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18 YcmZ1|^O~EDfq{XMm4OjRF)%U#039m