From nobody Sat Nov 23 18:13:43 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1732153863; cv=none; d=zohomail.com; s=zohoarc; b=VXDb2SFZ8D9kt0BlMQORGy2ei2tJvp6F6eyVTiehVvHQrkXftXZ+vibMF2p5I4cTDxOFmkrzRWBypd7EmJecGzsvFbIdGFDwpI1PRi4VokeAbM5le24fm64BdCrIErNb9FsmlKGX310oPbYfj1atySj1APCh2nKITBjAjfBAAIo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1732153863; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Sender:Subject:Subject:To:To:Message-Id; bh=uh839ebPB0g/yC4m1c+7zP4XxQLEQWTUp71oIl5bLVM=; b=cxL4U2IMbZxuEUXcrZFYshXLF3n1JXPnIkeL3NiQa8RvIVshq0lwIBYMu6B2K+4eyjpKhP490b+qFENQlh9gNSCaGr+TB8WyiYHYjnwL8DDdvVq+HYYu3n8Py5Zonv0Nfxg621uK+VbXmNNgRX61f0Ciy5fsAAGxI17ptXQ6ZkM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1732153863201484.7377241025248; Wed, 20 Nov 2024 17:51:03 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1tDwJy-0006yc-TR; Wed, 20 Nov 2024 20:49:26 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tDwJr-0006fh-9r for qemu-devel@nongnu.org; Wed, 20 Nov 2024 20:49:19 -0500 Received: from rev.ng ([94.130.142.21]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1tDwJm-0004re-Pq for qemu-devel@nongnu.org; Wed, 20 Nov 2024 20:49:19 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=rev.ng; s=dkim; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive:List-Unsubscribe:List-Unsubscribe-Post: List-Help; bh=uh839ebPB0g/yC4m1c+7zP4XxQLEQWTUp71oIl5bLVM=; b=CZLvMx2KenhHvkQ ozw0L2U86jJwRaiUbXevs6mFTMuwhX3btgPKPtJNY55EhT5TXiopCmwMfCRfum9etCE7gBk4R6ddS XyF9fB2xta5AmWQzCzTAbLLJ7RkK5DsrPAbQL7BppeDNmF/Ai4z/8rPZatnqCj5QHoYaPLEdBoMsA ag=; To: qemu-devel@nongnu.org Cc: ale@rev.ng, ltaylorsimpson@gmail.com, bcain@quicinc.com, richard.henderson@linaro.org, philmd@linaro.org, alex.bennee@linaro.org Subject: [RFC PATCH v1 30/43] helper-to-tcg: TcgGenPass, introduce TcgEmit.[cpp|h] Date: Thu, 21 Nov 2024 02:49:34 +0100 Message-ID: <20241121014947.18666-31-anjo@rev.ng> In-Reply-To: <20241121014947.18666-1-anjo@rev.ng> References: <20241121014947.18666-1-anjo@rev.ng> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=94.130.142.21; envelope-from=anjo@rev.ng; helo=rev.ng X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-to: Anton Johansson From: Anton Johansson via Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1732153864885116600 Content-Type: text/plain; charset="utf-8" A new translation unit is added with the purpose of containing all code which emits strings of TCG code. The idea is that maintainence of the backend will be simpler if all "tcg_*(*)" strings are contained and wrapped in functions. Signed-off-by: Anton Johansson --- .../helper-to-tcg/include/CmdLineOptions.h | 3 + subprojects/helper-to-tcg/meson.build | 1 + .../helper-to-tcg/passes/backend/TcgEmit.cpp | 1074 +++++++++++++++++ .../helper-to-tcg/passes/backend/TcgEmit.h | 290 +++++ .../helper-to-tcg/pipeline/Pipeline.cpp | 13 + 5 files changed, 1381 insertions(+) create mode 100644 subprojects/helper-to-tcg/passes/backend/TcgEmit.cpp create mode 100644 subprojects/helper-to-tcg/passes/backend/TcgEmit.h diff --git a/subprojects/helper-to-tcg/include/CmdLineOptions.h b/subprojec= ts/helper-to-tcg/include/CmdLineOptions.h index a5e467f8be..f59b700914 100644 --- a/subprojects/helper-to-tcg/include/CmdLineOptions.h +++ b/subprojects/helper-to-tcg/include/CmdLineOptions.h @@ -27,3 +27,6 @@ extern llvm::cl::opt TranslateAllHelpers; extern llvm::cl::opt TcgGlobalMappingsName; // Options for TcgTempAllocation extern llvm::cl::opt GuestPtrSize; +// Options for TcgEmit +extern llvm::cl::opt MmuIndexFunction; +extern llvm::cl::opt TempVectorBlock; diff --git a/subprojects/helper-to-tcg/meson.build b/subprojects/helper-to-= tcg/meson.build index ad3c307b6b..55a177bd94 100644 --- a/subprojects/helper-to-tcg/meson.build +++ b/subprojects/helper-to-tcg/meson.build @@ -51,6 +51,7 @@ sources =3D [ 'passes/PrepareForTcgPass/CanonicalizeIR.cpp', 'passes/PrepareForTcgPass/IdentityMap.cpp', 'passes/backend/TcgTempAllocationPass.cpp', + 'passes/backend/TcgEmit.cpp', ] =20 clang =3D bindir / 'clang' diff --git a/subprojects/helper-to-tcg/passes/backend/TcgEmit.cpp b/subproj= ects/helper-to-tcg/passes/backend/TcgEmit.cpp new file mode 100644 index 0000000000..6e3b6bdbd0 --- /dev/null +++ b/subprojects/helper-to-tcg/passes/backend/TcgEmit.cpp @@ -0,0 +1,1074 @@ +// +// Copyright(c) 2024 rev.ng Labs Srl. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, see . +// + +#include "TcgEmit.h" +#include "CmdLineOptions.h" +#include "backend/TcgType.h" + +using namespace llvm; + +// Counters used for prettier numbers in names +uint32_t VarIndex =3D 0; +bool EmittedVectorMem =3D false; + +namespace tcg +{ + +// Constant used to represent the MMU INDEX for all memory operations. +// get_tb_mmu_index is a function assumed to be defined by the target. +static const TcgV MmuIndex =3D + TcgV::makeImmediate(MmuIndexFunction + "(tcg_ctx->gen_tb->flags)", 32,= 32); + +void resetNameIndices() +{ + VarIndex =3D 0; + EmittedVectorMem =3D false; +} + +// TODO: do we still have manual makename calls? +const std::string mkName(const std::string Suffix) +{ + return Twine("v") + .concat(Suffix) + .concat("_") + .concat(Twine(VarIndex++)) + .str(); +} + +const std::string getType(const TcgV &Value) +{ + switch (Value.Kind) { + case IrValue: + case IrConst: + return Twine("TCGv_i").concat(Twine(Value.TcgSize)).str(); + case IrEnv: + return "TCGv_env"; + case IrImmediate: + if (Value.LlvmSize =3D=3D 1) { + return "bool"; + } else { + return Twine("int") + .concat(Twine((int)Value.LlvmSize)) + .concat("_t") + .str(); + } + case IrPtr: + return "TCGv_ptr"; + case IrPtrToOffset: + return "intptr_t"; + case IrLabel: + return "TCGLabel *"; + default: + abort(); + } +} + +inline StringRef mapPredicate(const CmpInst::Predicate &Pred) +{ + switch (Pred) { + case CmpInst::ICMP_EQ: + return "TCG_COND_EQ"; + case CmpInst::ICMP_NE: + return "TCG_COND_NE"; + case CmpInst::ICMP_UGT: + return "TCG_COND_GTU"; + case CmpInst::ICMP_UGE: + return "TCG_COND_GEU"; + case CmpInst::ICMP_ULT: + return "TCG_COND_LTU"; + case CmpInst::ICMP_ULE: + return "TCG_COND_LEU"; + case CmpInst::ICMP_SGT: + return "TCG_COND_GT"; + case CmpInst::ICMP_SGE: + return "TCG_COND_GE"; + case CmpInst::ICMP_SLT: + return "TCG_COND_LT"; + case CmpInst::ICMP_SLE: + return "TCG_COND_LE"; + default: + abort(); + } +} + +static std::string mapBinOp(const Instruction::BinaryOps &Opcode, + const TcgV &Src0, const TcgV &Src1) +{ + const bool IsImmediate =3D + (Src0.Kind =3D=3D IrImmediate or Src1.Kind =3D=3D IrImmediate); + const bool IsPtr =3D (Opcode =3D=3D Instruction::Add and + (Src0.Kind =3D=3D IrPtr or Src1.Kind =3D=3D IrPtr)= ); + assert(IsImmediate or Src0.TcgSize =3D=3D Src1.TcgSize); + std::string Expr =3D ""; + llvm::raw_string_ostream ExprStream(Expr); + + // Check for valid boolean operations if operating on a boolean + if (Src0.LlvmSize =3D=3D 1) { + assert(Src1.LlvmSize =3D=3D 1); + assert(Src0.TcgSize =3D=3D 32 or Src0.TcgSize =3D=3D 64); + assert(Src1.TcgSize =3D=3D 32 or Src1.TcgSize =3D=3D 64); + switch (Opcode) { + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + break; + default: + abort(); + } + } + + bool NeedSafe =3D false; + switch (Opcode) { + case Instruction::Add: + ExprStream << "tcg_gen_add"; + break; + case Instruction::Sub: + ExprStream << "tcg_gen_sub"; + break; + case Instruction::And: + ExprStream << "tcg_gen_and"; + break; + case Instruction::Or: + ExprStream << "tcg_gen_or"; + break; + case Instruction::Xor: + ExprStream << "tcg_gen_xor"; + break; + case Instruction::Mul: + ExprStream << "tcg_gen_mul"; + break; + case Instruction::UDiv: + ExprStream << "tcg_gen_divu"; + break; + case Instruction::SDiv: + ExprStream << "tcg_gen_div"; + break; + case Instruction::AShr: + ExprStream << "tcg_gen_sar"; + NeedSafe =3D true; + break; + case Instruction::LShr: + ExprStream << "tcg_gen_shr"; + NeedSafe =3D true; + break; + case Instruction::Shl: + ExprStream << "tcg_gen_shl"; + NeedSafe =3D true; + break; + default: + abort(); + } + NeedSafe =3D false; + + if (IsImmediate) { + ExprStream << "i"; + } + + if (IsPtr) { + ExprStream << "_ptr"; + } else { + ExprStream << "_i" << (int)Src0.TcgSize; + } + + if (IsImmediate and NeedSafe) { + ExprStream << "_safe" << (int)Src0.TcgSize; + } + + ExprStream.flush(); + + return Expr; +} + +static std::string mapVecBinOp(const Instruction::BinaryOps &Opcode, + const TcgV &Src0, const TcgV &Src1) +{ + const bool IsShift =3D Opcode =3D=3D Instruction::Shl or + Opcode =3D=3D Instruction::LShr or + Opcode =3D=3D Instruction::AShr; + + std::string Suffix; + switch (Src1.Kind) { + case IrPtrToOffset: + Suffix =3D (IsShift) ? "v" : ""; + break; + case IrConst: + case IrValue: + Suffix =3D "s"; + break; + case IrImmediate: + Suffix =3D "i"; + break; + default: + abort(); + } + + switch (Opcode) { + case Instruction::Add: + return "add" + Suffix; + break; + case Instruction::Sub: + return "sub" + Suffix; + break; + case Instruction::Mul: + return "mul" + Suffix; + break; + case Instruction::And: + return "and" + Suffix; + break; + case Instruction::Or: + return "or" + Suffix; + break; + case Instruction::Xor: + return "xor" + Suffix; + break; + case Instruction::Shl: + return "shl" + Suffix; + break; + case Instruction::LShr: + return "shr" + Suffix; + break; + case Instruction::AShr: + return "sar" + Suffix; + break; + default: + abort(); + } +} + +void tempNew(raw_ostream &Out, const TcgV &Value) +{ + if (Value.Kind =3D=3D IrValue) { + Out << "tcg_temp_new_i" << (int)Value.TcgSize << "();\n"; + } +} + +void tempNewPtr(raw_ostream &Out) { Out << "tcg_temp_new_ptr();\n"; } + +void tempNewVec(raw_ostream &Out, uint32_t Size) +{ + Out << "temp_new_gvec(&mem, " << Size << ");\n"; +} + +void genNewLabel(raw_ostream &Out) { Out << "gen_new_label();\n"; } + +void genSetLabel(raw_ostream &Out, const TcgV &L) +{ + assert(L.Kind =3D=3D IrLabel); + Out << "gen_set_label(" << L << ");\n"; +} + +void defineNewTemp(raw_ostream &Out, const TcgV &Tcg) +{ + assert(!Tcg.ConstantExpression); + if (Tcg.Kind =3D=3D IrPtrToOffset and !EmittedVectorMem) { + EmittedVectorMem =3D true; + c::emitVectorMemVar(Out); + } + Out << tcg::getType(Tcg) << " " << Tcg << " =3D "; + switch (Tcg.Kind) { + case IrValue: + tcg::tempNew(Out, Tcg); + break; + case IrPtr: + tcg::tempNewPtr(Out); + break; + case IrPtrToOffset: + tcg::tempNewVec(Out, vectorSizeInBytes(Tcg)); + break; + case IrLabel: + tcg::genNewLabel(Out); + break; + default: + abort(); + } +} + +void genBr(raw_ostream &Out, const TcgV &L) +{ + assert(L.Kind =3D=3D IrLabel); + Out << "tcg_gen_br(" << L << ");\n"; +} + +void genTempInit(raw_ostream &Out, const TcgV &Arg1, const StringRef Str) +{ + Out << getType(Arg1) << ' ' << Arg1 << " =3D " << "tcg_const_i" + << (int)Arg1.TcgSize << "(" << Str << ");\n"; +} + +void genTempInit(raw_ostream &Out, const TcgV &Arg1, uint64_t Value) +{ + Out << getType(Arg1) << ' ' << Arg1 << " =3D " << "tcg_const_i" + << (int)Arg1.TcgSize << "((uint64_t)" << Value << "ULL);\n"; +} + +void genTempInit(raw_ostream &Out, const TcgV &Arg1, const TcgV &Arg2) +{ + assert(Arg2.Kind =3D=3D IrImmediate); + Out << getType(Arg1) << ' ' << Arg1 << " =3D " << "tcg_const_i" + << (int)Arg1.TcgSize << "(" << Arg2 << ");\n"; +} + +void genAssignConst(raw_ostream &Out, const TcgV &Arg1, const StringRef St= r) +{ + Out << getType(Arg1) << ' ' << Arg1 << " =3D " << "tcg_constant_i" + << (int)Arg1.TcgSize << "(" << Str << ");\n"; +} + +void genAssignConst(raw_ostream &Out, const TcgV &Arg1, uint64_t Value) +{ + Out << getType(Arg1) << ' ' << Arg1 << " =3D " << "tcg_constant_i" + << (int)Arg1.TcgSize << "((uint64_t)" << Value << "ULL);\n"; +} + +void genAssignConst(raw_ostream &Out, const TcgV &Arg1, const TcgV &Arg2) +{ + assert(Arg2.Kind =3D=3D IrImmediate); + Out << getType(Arg1) << ' ' << Arg1 << " =3D " << "tcg_constant_i" + << (int)Arg1.TcgSize << "(" << Arg2 << ");\n"; +} + +void genExtI32I64(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.TcgSize =3D=3D 64); + assert(Src.TcgSize =3D=3D 32); + emitCallTcg(Out, "tcg_gen_ext_i32_i64", {Dst, Src}); +} + +void genExtrlI64I32(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.TcgSize =3D=3D 32); + assert(Src.TcgSize =3D=3D 64); + emitCallTcg(Out, "tcg_gen_extrl_i64_i32", {Dst, Src}); +} + +void genExtuI32I64(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.TcgSize =3D=3D 64); + assert(Src.TcgSize =3D=3D 32); + emitCallTcg(Out, "tcg_gen_extu_i32_i64", {Dst, Src}); +} + +void genExtrhI64I32(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.TcgSize =3D=3D 32); + assert(Src.TcgSize =3D=3D 64); + emitCallTcg(Out, "tcg_gen_extrh_i64_i32", {Dst, Src}); +} + +void genExtract(raw_ostream &Out, bool Sign, const TcgV &Dst, const TcgV &= Src, + const TcgV &Offset, const TcgV &Length) +{ + assert(Dst.TcgSize =3D=3D Src.TcgSize); + const char *SignStr =3D (Sign) ? "s" : ""; + const TcgV &MSrc =3D materialize(Src); + Out << "tcg_gen_" << SignStr << "extract_i" << (int)Dst.TcgSize << "("; + emitArgListTcg(Out, {Dst, MSrc, Offset, Length}); + Out << ");\n"; +} + +void genDeposit(raw_ostream &Out, const TcgV &Dst, const TcgV &Into, + const TcgV &From, const TcgV &Offset, const TcgV &Length) +{ + assert(Dst.TcgSize =3D=3D Into.TcgSize); + assert(Dst.TcgSize =3D=3D From.TcgSize or From.Kind =3D=3D IrImmediate= ); + Out << "tcg_gen_deposit_i" << (int)Dst.TcgSize << "("; + const TcgV MInto =3D materialize(Into); + const TcgV MLength =3D materialize(Length); + emitArgListTcg(Out, {Dst, MInto, MLength, From, Offset}); + Out << ");\n"; +} + +void genTruncPtr(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + auto FuncStr =3D Twine("tcg_gen_trunc_i") + .concat(std::to_string(Src.TcgSize)) + .concat("_ptr") + .str(); + emitCallTcg(Out, FuncStr, {Dst, Src}); +} + +void genConcat(raw_ostream &Out, const TcgV &Dst, const TcgV &Src1, + const TcgV &Src2) +{ + assert(Dst.TcgSize =3D=3D 64); + assert(Src1.TcgSize =3D=3D 32); + assert(Src2.TcgSize =3D=3D 32); + emitCallTcg(Out, "tcg_gen_concat_i32_i64", {Dst, Src1, Src2}); +} + +void genMov(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.TcgSize =3D=3D Src.TcgSize); + Out << "tcg_gen_mov_i" << (int)Dst.TcgSize << "(" << Dst << ", " << Src + << ");\n"; +} + +void genMovPtr(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.TcgSize =3D=3D Src.TcgSize); + assert(Dst.Kind =3D=3D IrPtr); + assert(Src.Kind =3D=3D IrPtr); + Out << "tcg_gen_mov_ptr(" << Dst << ", " << Src << ");\n"; +} + +void genAddPtr(raw_ostream &Out, const TcgV &Dst, const TcgV &Ptr, + const TcgV &Offset) +{ + assert(Ptr.Kind =3D=3D IrPtr or Ptr.Kind =3D=3D IrEnv); + switch (Offset.Kind) { + case IrConst: + case IrImmediate: { + emitCallTcg(Out, "tcg_gen_addi_ptr", {Dst, Ptr, Offset}); + } break; + case IrValue: { + uint32_t TcgTargetPtrSize =3D 64; + auto OffsetPtr =3D + TcgV::makeTemp(TcgTargetPtrSize, TcgTargetPtrSize, IrPtr); + tcg::defineNewTemp(Out, OffsetPtr); + tcg::genTruncPtr(Out, OffsetPtr, Offset); + + emitCallTcg(Out, "tcg_gen_add_ptr", {Dst, Ptr, OffsetPtr}); + } break; + default: + abort(); + } +} + +void genBinOp(raw_ostream &Out, const TcgV &Dst, + const Instruction::BinaryOps Opcode, const TcgV &Src0, + const TcgV &Src1) +{ + auto OpStr =3D mapBinOp(Opcode, Src0, Src1); + emitCallTcg(Out, OpStr, {Dst, Src0, Src1}); +} + +void genMovI(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Src.Kind =3D=3D IrImmediate); + Out << "tcg_gen_movi_i" << (int)Dst.TcgSize << "(" << Dst << ", " << S= rc + << ");\n"; +} + +void genMovcond(raw_ostream &Out, const CmpInst::Predicate &Pred, + const TcgV &Ret, const TcgV &C1, const TcgV &C2, const Tcg= V &V1, + const TcgV &V2) +{ + assert(Ret.TcgSize =3D=3D C1.TcgSize); + assert(Ret.TcgSize =3D=3D C2.TcgSize); + assert(Ret.TcgSize =3D=3D V1.TcgSize); + assert(Ret.TcgSize =3D=3D V2.TcgSize); + const TcgV mC1 =3D materialize(C1); + const TcgV mC2 =3D materialize(C2); + const TcgV mV1 =3D materialize(V1); + const TcgV mV2 =3D materialize(V2); + Out << "tcg_gen_movcond_i" << (int)Ret.TcgSize << '(' << mapPredicate(= Pred) + << ", "; + emitArgListTcg(Out, {Ret, mC1, mC2, mV1, mV2}); + Out << ");\n"; +} + +void genSetcond(raw_ostream &Out, const CmpInst::Predicate &Pred, + const TcgV &Dst, const TcgV &Op1, const TcgV &Op2) +{ + assert(Op1.TcgSize =3D=3D Op2.TcgSize); + assert(Op1.TcgSize =3D=3D Dst.TcgSize); + assert(Op1.TcgSize =3D=3D 32 or Op1.TcgSize =3D=3D 64); + Out << "tcg_gen_setcond_i" << (int)Dst.TcgSize << "(" << mapPredicate(= Pred) + << ", " << Dst << ", " << Op1 << ", " << Op2 << ");\n"; +} + +void genSetcondI(raw_ostream &Out, const CmpInst::Predicate &Pred, + const TcgV &Dst, const TcgV &Op1, const TcgV &Op2) +{ + assert(Op1.TcgSize =3D=3D Dst.TcgSize); + assert(Op1.TcgSize =3D=3D 32 or Op1.TcgSize =3D=3D 64); + assert(Dst.Kind !=3D IrImmediate && Op1.Kind !=3D IrImmediate && + Op2.Kind =3D=3D IrImmediate); + Out << "tcg_gen_setcondi_i" << (int)Dst.TcgSize << "(" << mapPredicate= (Pred) + << ", " << Dst << ", " << Op1 << ", " << Op2 << ");\n"; +} + +void genBrcond(raw_ostream &Out, const CmpInst::Predicate &Pred, + const TcgV &Arg1, const TcgV &Arg2, const TcgV &Label) +{ + assert(Arg1.TcgSize =3D=3D Arg2.TcgSize); + assert(Arg1.TcgSize =3D=3D 32 || Arg1.TcgSize =3D=3D 64); + assert(Label.Kind =3D=3D IrLabel); + if (Arg2.Kind =3D=3D IrImmediate) { + Out << "tcg_gen_brcondi_i" << (int)Arg1.TcgSize; + } else { + Out << "tcg_gen_brcond_i" << (int)Arg1.TcgSize; + } + Out << "(" << mapPredicate(Pred) << ", " << materialize(Arg1) << ", " + << Arg2 << ", " << Label << ");\n"; +} + +void genQemuLoad(raw_ostream &Out, const TcgV &Dst, const TcgV &Ptr, + const char *MemOpStr) +{ + assert(Dst.Kind =3D=3D IrValue); + assert(Ptr.Kind !=3D IrImmediate); + const auto MPtr =3D materialize(Ptr); + Out << "tcg_gen_qemu_ld_i" << (int)Dst.TcgSize << "("; + emitArgListTcg(Out, {Dst, MPtr, MmuIndex}); + Out << ", " << MemOpStr << ");\n"; +} + +void genQemuStore(raw_ostream &Out, const TcgV &Ptr, const TcgV &Src, + const char *MemOpStr) +{ + assert(Src.Kind =3D=3D IrValue); + assert(Ptr.Kind !=3D IrImmediate); + const auto MPtr =3D materialize(Ptr); + Out << "tcg_gen_qemu_st_i" << (int)Src.TcgSize << "("; + emitArgListTcg(Out, {Src, MPtr, MmuIndex}); + Out << ", " << MemOpStr << ");\n"; +} + +void genLd(raw_ostream &Out, const TcgV &Dst, const TcgV &Ptr, uint64_t Of= fset) +{ + assert(Ptr.Kind =3D=3D IrPtr); + // First output the correct tcg function for the widths of Dst + if (Dst.LlvmSize < Dst.TcgSize) { + Out << "tcg_gen_ld" << (int)Dst.LlvmSize << "u_i" << (int)Dst.TcgS= ize; + } else { + Out << "tcg_gen_ld_i" << (int)Dst.TcgSize; + } + // Then emit params + Out << "(" << Dst << ", " << Ptr << ", " << Offset << ");\n"; +} + +void genSt(raw_ostream &Out, const TcgV &Ptr, const TcgV &Src, uint64_t Of= fset) +{ + assert(Ptr.Kind =3D=3D IrPtr); + // First output the correct tcg function for the widths of Dst + if (Src.LlvmSize < Src.TcgSize) { + Out << "tcg_gen_st" << (int)Src.LlvmSize << "_i" << (int)Src.TcgSi= ze; + } else { + Out << "tcg_gen_st_i" << (int)Src.TcgSize; + } + // Then emit params + Out << "(" << Src << ", " << Ptr << ", " << Offset << ");\n"; +} +void genFunnelShl(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1, const TcgV &Shift) +{ + assert(Src0.TcgSize =3D=3D Dst.TcgSize); + assert(Src1.TcgSize =3D=3D Dst.TcgSize); + assert(Shift.TcgSize =3D=3D Dst.TcgSize); + + if (Dst.TcgSize =3D=3D 32) { + auto Temp =3D TcgV::makeTemp(64, 64, IrValue); + defineNewTemp(Out, Temp); + genConcat(Out, Temp, Src1, Src0); + + if (Shift.Kind =3D=3D IrImmediate) { + genBinOp(Out, Temp, Instruction::Shl, Temp, Shift); + } else { + auto Ext =3D TcgV::makeTemp(64, 64, IrValue); + defineNewTemp(Out, Ext); + genExtuI32I64(Out, Ext, Shift); + genBinOp(Out, Temp, Instruction::Shl, Temp, Ext); + } + + tcg::genExtrhI64I32(Out, Dst, Temp); + } else if (Dst.TcgSize =3D=3D 64) { + const TcgV ASrc0 =3D materialize(Src0); + const TcgV ASrc1 =3D materialize(Src1); + const TcgV AShift =3D materialize(Shift); + genCallHelper(Out, "helper_fshl_i64", {Dst, ASrc0, ASrc1, AShift}); + } +} + +void genBitreverse(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + auto FuncName =3D Twine("helper_bitreverse") + .concat(Twine((int)Dst.LlvmSize)) + .concat("_i") + .concat(Twine((int)Src.TcgSize)) + .str(); + genCallHelper(Out, FuncName, {Dst, Src}); +} + +void genCountLeadingZeros(raw_ostream &Out, const TcgV &Dst, const TcgV &S= rc) +{ + assert(Dst.TcgSize =3D=3D Src.TcgSize); + Out << "tcg_gen_clzi_i" << (int)Dst.TcgSize << "(" << Dst << ", " << S= rc + << ", " << (int)Src.TcgSize << ");\n"; +} + +void genCountTrailingZeros(raw_ostream &Out, const TcgV &Dst, const TcgV &= Src) +{ + assert(Dst.TcgSize =3D=3D Src.TcgSize); + Out << "tcg_gen_ctzi_i" << (int)Dst.TcgSize << "(" << Dst << ", " << S= rc + << ", " << (int)Src.TcgSize << ");\n"; +} + +void genCountOnes(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.TcgSize =3D=3D Src.TcgSize); + Out << "tcg_gen_ctpop_i" << (int)Dst.TcgSize << "(" << Dst << ", " << = Src + << ");\n"; +} + +void genByteswap(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.TcgSize =3D=3D Src.TcgSize); + Out << "tcg_gen_bswap" << (int)Dst.TcgSize << "_i" << (int)Src.TcgSize + << "(" << Dst << ", " << Src << ");\n"; +} + +static void genVecBinOpStr(raw_ostream &Out, StringRef Op, const TcgV &Dst, + const TcgV &Src0, const TcgV &Src1) +{ + const uint32_t VectorSizeInBytes =3D vectorSizeInBytes(Dst); + Out << "tcg_gen_gvec_"; + Out << Op; + Out << "(MO_" << (int)Dst.LlvmSize << ", " << Dst << ", " << Src0 << "= , " + << Src1 << ", " << VectorSizeInBytes << ", " << VectorSizeInBytes + << ");\n"; +} + +void genVecBinOp(raw_ostream &Out, const Instruction::BinaryOps Opcode, + const TcgV &Dst, const TcgV &Src0, const TcgV &Src1) +{ + genVecBinOpStr(Out, mapVecBinOp(Opcode, Src0, Src1), Dst, Src0, Src1); +} + +void genVecSignedSatAdd(raw_ostream &Out, const TcgV &Dst, const TcgV &Src= 0, + const TcgV &Src1) +{ + assert(Dst.Kind =3D=3D IrPtrToOffset); + genVecBinOpStr(Out, "ssadd", Dst, Src0, Src1); +} + +void genVecSignedSatSub(raw_ostream &Out, const TcgV &Dst, const TcgV &Src= 0, + const TcgV &Src1) +{ + assert(Dst.Kind =3D=3D IrPtrToOffset); + genVecBinOpStr(Out, "sssub", Dst, Src0, Src1); +} + +void genVecSignedMax(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1) +{ + switch (Dst.Kind) { + case IrValue: { + const TcgV MSrc0 =3D materialize(Src0); + const TcgV MSrc1 =3D materialize(Src1); + Out << "tcg_gen_smax_i" << (int)Dst.TcgSize << "(" << Dst << ", " + << MSrc0 << ", " << MSrc1 << ");\n"; + } break; + case IrPtrToOffset: { + genVecBinOpStr(Out, "smax", Dst, Src0, Src1); + } break; + default: + abort(); + } +} + +void genVecUnsignedMax(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1) +{ + switch (Dst.Kind) { + case IrValue: { + const TcgV MSrc0 =3D materialize(Src0); + const TcgV MSrc1 =3D materialize(Src1); + Out << "tcg_gen_umax_i" << (int)Dst.TcgSize << "(" << Dst << ", " + << MSrc0 << ", " << MSrc1 << ");\n"; + } break; + case IrPtrToOffset: { + genVecBinOpStr(Out, "umax", Dst, Src0, Src1); + } break; + default: + abort(); + } +} + +void genVecSignedMin(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1) +{ + switch (Dst.Kind) { + case IrValue: { + const TcgV MSrc0 =3D materialize(Src0); + const TcgV MSrc1 =3D materialize(Src1); + Out << "tcg_gen_smin_i" << (int)Dst.TcgSize << "(" << Dst << ", " + << MSrc0 << ", " << MSrc1 << ");\n"; + } break; + case IrPtrToOffset: { + genVecBinOpStr(Out, "smin", Dst, Src0, Src1); + } break; + default: + abort(); + } +} + +void genVecUnsignedMin(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1) +{ + switch (Dst.Kind) { + case IrValue: { + const TcgV MSrc0 =3D materialize(Src0); + const TcgV MSrc1 =3D materialize(Src1); + Out << "tcg_gen_umin_i" << (int)Dst.TcgSize << "(" << Dst << ", " + << MSrc0 << ", " << MSrc1 << ");\n"; + } break; + case IrPtrToOffset: { + genVecBinOpStr(Out, "umin", Dst, Src0, Src1); + } break; + default: + assert(false); + } +} + +void genVecMemcpy(raw_ostream &Out, const TcgV &Dst, const TcgV &Src, + const TcgV &Size) +{ + Out << "tcg_gen_gvec_mov(MO_8" << ", " << Dst << ", " << Src << ", " <= < Size + << ", " << Size << ");\n"; +} + +void genVecMemset(raw_ostream &Out, const TcgV &Dst, const TcgV &Src, + const TcgV &Size) +{ + switch (Src.Kind) { + case IrValue: + case IrConst: + Out << "tcg_gen_gvec_dup_i" << (int)Src.TcgSize << "(MO_" + << (int)Src.LlvmSize << ", " << Dst << ", " << Size << ", " <<= Size + << ", " << Src << ");\n"; + break; + case IrImmediate: + Out << "tcg_gen_gvec_dup_imm" << "(MO_" << (int)Src.LlvmSize << ",= " + << Dst << ", " << Size << ", " << Size << ", " << Src << ");\n= "; + break; + default: + abort(); + } +} + +void genVecSplat(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + const uint32_t VectorSizeInBytes =3D vectorSizeInBytes(Dst); + const auto Size =3D + TcgV::makeImmediate(Twine(VectorSizeInBytes).str(), 64, 64); + genVecMemset(Out, Dst, Src, Size); +} + +void genVecArrSplat(raw_ostream &Out, const TcgV &Dst, + SmallVector &Arr) +{ + const uint32_t VectorSizeInBytes =3D vectorSizeInBytes(Dst); + const std::string TmpName =3D mkName("varr"); + Out << "uint" << (int)Dst.LlvmSize << "_t " << TmpName << "[] =3D {"; + emitArgListTcg(Out, Arr.begin(), Arr.end()); + Out << "};\n"; + // TODO: We are using global tcg_env here as not all functions that mi= ght + // emit constants take env. + Out << "tcg_gen_gvec_constant(MO_" << (int)Dst.LlvmSize << ", tcg_env" + << ", " << Dst << ", " << TmpName << ", " << VectorSizeInBytes + << ");\n"; +} + +void genVecBitsel(raw_ostream &Out, const TcgV &Dst, const TcgV &Cond, + const TcgV &Src0, const TcgV &Src1) +{ + const uint32_t VectorSizeInBytes =3D vectorSizeInBytes(Dst); + Out << "tcg_gen_gvec_bitsel(" << "MO_" << (int)Dst.LlvmSize << ", " <<= Dst + << ", " << Cond << ", " << Src0 << ", " << Src1 << ", " + << VectorSizeInBytes << ", " << VectorSizeInBytes << ");\n"; +} + +void genVecCmp(raw_ostream &Out, const TcgV &Dst, + const CmpInst::Predicate &Pred, const TcgV &Src0, + const TcgV &Src1) +{ + // TODO: Return type of llvm vector compare is actually 128 x i1, curr= ently + // we keep the same element size. Requires trunc. + const uint32_t VectorSizeInBytes =3D vectorSizeInBytes(Dst); + Out << "tcg_gen_gvec_cmp(" << mapPredicate(Pred) << ", " << "MO_" + << (int)Dst.LlvmSize << ", " << Dst << ", " << Src0 << ", " << Src1 + << ", " << VectorSizeInBytes << ", " << VectorSizeInBytes << ");\n= "; +} + +void genAbs(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + assert(Dst.Kind =3D=3D Src.Kind); + assert(Dst.TcgSize =3D=3D Src.TcgSize); + switch (Dst.Kind) { + case IrValue: { + const auto FuncStr =3D + Twine("tcg_gen_abs_i").concat(Twine(Src.TcgSize)).str(); + emitCallTcg(Out, FuncStr, {Dst, Src}); + } break; + case IrPtrToOffset: { + auto VectorSize =3D Dst.LlvmSize * Dst.VectorElementCount / 8; + Out << "tcg_gen_gvec_abs(" << "MO_" << (int)Dst.LlvmSize << ", " <= < Dst + << ", " << Src << ", " << VectorSize << ", " << VectorSize + << ");\n"; + } break; + default: + abort(); + } +} + +void genVecNot(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + const uint32_t VectorSize =3D Dst.LlvmSize * Dst.VectorElementCount / = 8; + Out << "tcg_gen_gvec_not(MO_" << (int)Src.LlvmSize << ", " << Dst << "= , " + << Src << ", " << VectorSize << ", " << VectorSize << ");\n"; +} + +static void genVecSizeChange(raw_ostream &Out, StringRef Name, const TcgV = &Dst, + const TcgV &Src) +{ + auto DstSz =3D vectorSizeInBytes(Dst); + auto SrcSz =3D vectorSizeInBytes(Src); + Out << "tcg_gen_gvec_" << Name << "(MO_" << (int)Dst.LlvmSize << ", MO= _" + << (int)Src.LlvmSize << ", " << Dst << ", " << Src << ", " << DstSz + << ", " << SrcSz << ", " << DstSz << ");\n"; +} + +void genVecTrunc(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + genVecSizeChange(Out, "trunc", Dst, Src); +} + +void genVecSext(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + genVecSizeChange(Out, "sext", Dst, Src); +} + +void genVecZext(raw_ostream &Out, const TcgV &Dst, const TcgV &Src) +{ + genVecSizeChange(Out, "zext", Dst, Src); +} + +} // namespace tcg + +namespace c +{ + +inline StringRef mapCPredicate(const CmpInst::Predicate &Pred) +{ + switch (Pred) { + case CmpInst::ICMP_EQ: + return "=3D=3D"; + case CmpInst::ICMP_NE: + return "!=3D"; + case CmpInst::ICMP_UGT: + return ">"; + case CmpInst::ICMP_UGE: + return ">=3D"; + case CmpInst::ICMP_ULT: + return "<"; + case CmpInst::ICMP_ULE: + return "<=3D"; + case CmpInst::ICMP_SGT: + return ">"; + case CmpInst::ICMP_SGE: + return ">=3D"; + case CmpInst::ICMP_SLT: + return "<"; + case CmpInst::ICMP_SLE: + return "<=3D"; + default: + abort(); + } +} + +enum BinOpSrcCast { + CastNone, + CastSigned, + CastUnsigned, +}; + +static std::string mapBinOp(const Instruction::BinaryOps &Opcode, + const TcgV &Src0, const TcgV &Src1) +{ + assert(Src0.Kind =3D=3D IrImmediate and Src1.Kind =3D=3D IrImmediate); + std::string Op; + BinOpSrcCast CastSrc0 =3D CastNone; + BinOpSrcCast CastSrc1 =3D CastNone; + switch (Opcode) { + case Instruction::Add: + Op =3D "+"; + break; + case Instruction::And: + Op =3D "&"; + break; + case Instruction::AShr: + CastSrc0 =3D CastUnsigned; + Op =3D ">>"; + break; + case Instruction::LShr: + CastSrc0 =3D CastSigned; + Op =3D ">>"; + break; + case Instruction::Shl: + Op =3D "<<"; + break; + case Instruction::Mul: + Op =3D "*"; + break; + case Instruction::UDiv: + CastSrc0 =3D CastUnsigned; + CastSrc1 =3D CastUnsigned; + Op =3D "/"; + break; + case Instruction::SDiv: + CastSrc0 =3D CastSigned; + CastSrc1 =3D CastSigned; + Op =3D "/"; + break; + case Instruction::Or: + Op =3D "|"; + break; + case Instruction::Sub: + Op =3D "-"; + break; + case Instruction::Xor: + Op =3D "^"; + break; + default: + abort(); + } + + std::string Expr =3D ""; + llvm::raw_string_ostream ExprStream(Expr); + ExprStream << "("; + if (CastSrc0 !=3D CastNone) { + auto IntPrefix =3D (CastSrc0 =3D=3D CastSigned) ? "int" : "uint"; + ExprStream << "(" << IntPrefix << (int)Src0.LlvmSize << "_t) "; + } + ExprStream << Src0 << " " << Op << " "; + if (CastSrc1 !=3D CastNone) { + auto IntPrefix =3D (CastSrc1 =3D=3D CastSigned) ? "int" : "uint"; + ExprStream << "(" << IntPrefix << (int)Src1.LlvmSize << "_t) "; + } + ExprStream << Src1 << ")"; + ExprStream.flush(); + + return Expr; +} + +TcgV ptrAdd(const TcgV &Ptr, const TcgV &Offset) +{ + assert(Offset.Kind =3D=3D IrConst or Offset.Kind =3D=3D IrImmediate); + switch (Ptr.Kind) { + case IrConst: + case IrImmediate: { + std::string Expr =3D ""; + llvm::raw_string_ostream ExprStream(Expr); + ExprStream << "(uint" << (int)Ptr.TcgSize << "_t *) ((uintptr_t) " + << Ptr << " + " << Offset << ")"; + ExprStream.flush(); + return TcgV::makeImmediate(Expr, Ptr.TcgSize, Ptr.LlvmSize); + } break; + case IrPtrToOffset: { + std::string Expr =3D ""; + llvm::raw_string_ostream ExprStream(Expr); + ExprStream << "(" << Ptr << " + " << Offset << ")"; + ExprStream.flush(); + TcgV Tcg(Expr, Ptr.TcgSize, Ptr.LlvmSize, Ptr.VectorElementCount, + IrPtrToOffset); + Tcg.ConstantExpression =3D true; + return Tcg; + } break; + default: + abort(); + } +} + +TcgV ternary(const TcgV &Cond, const TcgV &True, const TcgV &False) +{ + assert(Cond.Kind =3D=3D IrImmediate); + std::string Expr =3D ""; + llvm::raw_string_ostream ExprStream(Expr); + ExprStream << "(" << Cond << " ? " << True << " : " << False << ")"; + ExprStream.flush(); + return TcgV::makeImmediate(Expr, True.TcgSize, True.LlvmSize); +} + +TcgV deref(const TcgV &Ptr, uint32_t LlvmSize, uint32_t TcgSize) +{ + assert(Ptr.Kind =3D=3D IrImmediate); + std::string Expr =3D Twine("*").concat(tcg::getName(Ptr)).str(); + return TcgV::makeImmediate(Expr, TcgSize, LlvmSize); +} + +TcgV compare(const CmpInst::Predicate &Pred, const TcgV &Src0, const TcgV = &Src1) +{ + assert(Src0.Kind =3D=3D IrImmediate and Src1.Kind =3D=3D IrImmediate); + std::string Expr =3D ""; + llvm::raw_string_ostream ExprStream(Expr); + ExprStream << "(" << Src0 << " " << mapCPredicate(Pred) << " " << Src1 + << ")"; + ExprStream.flush(); + return TcgV::makeImmediate(Expr, Src0.TcgSize, 1); +} + +TcgV zext(const TcgV &V, uint32_t LlvmSize, uint32_t TcgSize) +{ + assert(V.Kind =3D=3D IrImmediate); + std::string Expr =3D ""; + llvm::raw_string_ostream ExprStream(Expr); + ExprStream << "((uint" << (int)LlvmSize << "_t) (uint" << (int)V.TcgSi= ze + << "_t) " << V << ")"; + ExprStream.flush(); + return TcgV::makeImmediate(Expr, TcgSize, LlvmSize); +} + +TcgV sext(const TcgV &V, uint32_t LlvmSize, uint32_t TcgSize) +{ + assert(V.Kind =3D=3D IrImmediate); + std::string Expr =3D ""; + llvm::raw_string_ostream ExprStream(Expr); + ExprStream << "((int" << (int)LlvmSize << "_t) (int" << (int)V.TcgSize + << "_t) " << V << ")"; + ExprStream.flush(); + return TcgV::makeImmediate(Expr, TcgSize, LlvmSize); +} + +TcgV binop(Instruction::BinaryOps Opcode, const TcgV &Src0, const TcgV &Sr= c1) +{ + std::string Op =3D mapBinOp(Opcode, Src0, Src1); + uint32_t LargestLlvmSize =3D std::max(Src0.LlvmSize, Src1.LlvmSize); + uint32_t LargestTcgSize =3D llvmToTcgSize(LargestLlvmSize); + return TcgV::makeImmediate(Op, LargestTcgSize, LargestLlvmSize); +} + +void emitVectorPreamble(raw_ostream &Out) +{ + Out << "typedef struct VectorMem {\n"; + Out << " uint32_t allocated;\n"; + Out << "} VectorMem;\n\n"; + + Out << "static intptr_t temp_new_gvec(VectorMem *mem, uint32_t size)\n= "; + Out << "{\n"; + Out << " uint32_t off =3D ROUND_UP(mem->allocated, size);\n"; + Out << " g_assert(off + size <=3D STRUCT_ARRAY_SIZE(CPUArchState, " + "tmp_vmem));\n"; + Out << " mem->allocated =3D off + size;\n"; + Out << " return offsetof(CPUArchState, " << TempVectorBlock + << ") + off;\n"; + Out << "}\n"; +} + +void emitVectorMemVar(raw_ostream &Out) { Out << "VectorMem mem =3D {0};\n= "; } + +} // namespace c diff --git a/subprojects/helper-to-tcg/passes/backend/TcgEmit.h b/subprojec= ts/helper-to-tcg/passes/backend/TcgEmit.h new file mode 100644 index 0000000000..5e35efbaa1 --- /dev/null +++ b/subprojects/helper-to-tcg/passes/backend/TcgEmit.h @@ -0,0 +1,290 @@ +// +// Copyright(c) 2024 rev.ng Labs Srl. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, see . +// + +#pragma once + +#include "TcgType.h" + +#include +#include +#include // for CmpInst::Predicate +#include +#include +#include + +#include + +using llvm::CmpInst; +using llvm::Instruction; +using llvm::raw_ostream; +using llvm::SmallVector; +using llvm::StringRef; +using llvm::Twine; +using llvm::Value; + +namespace tcg +{ +inline std::string getName(const TcgV &V) +{ + if (V.ConstantExpression or V.Kind =3D=3D IrImmediate or V.Kind =3D=3D= IrConst) { + return V.Name; + } else { + switch (V.Kind) { + case IrValue: + return Twine("temp").concat(Twine(V.Id)).str(); + case IrEnv: + return "env"; + case IrPtr: + return Twine("ptr").concat(Twine(V.Id)).str(); + case IrPtrToOffset: + return Twine("vec").concat(Twine(V.Id)).str(); + case IrLabel: + return Twine("label").concat(Twine(V.Id)).str(); + default: + abort(); + }; + } +} +} // namespace tcg + +inline raw_ostream &operator<<(raw_ostream &Out, const TcgV &V) +{ + Out << tcg::getName(V); + return Out; +} + +namespace tcg +{ + +// TODO: The names we give temporaries depend on the function we're in, +// maybe we can put this name/index stuff somewhere more relevant? +void resetNameIndices(); +const std::string mkName(const std::string Suffix); + +// String representation of types +const std::string getType(const TcgV &Value); + +inline const TcgV materialize(const TcgV &Value) +{ + if (Value.Kind !=3D IrImmediate) { + return Value; + } + TcgV M =3D Value; + M.Name =3D Twine("tcg_constant_i") + .concat(Twine((int)Value.TcgSize)) + .concat("(") + .concat(tcg::getName(Value)) + .concat(")") + .str(); + M.Kind =3D IrConst; + return M; +} + +template +void emitArgListTcg(raw_ostream &Out, const I Beg, const I End) +{ + auto It =3D Beg; + if (It !=3D End) { + Out << *It; + ++It; + } + while (It !=3D End) { + Out << ", " << *It; + ++It; + } +} + +template +void emitCall(raw_ostream &Out, const StringRef &S, const I Beg, const I E= nd) +{ + Out << S << '('; + auto It =3D Beg; + if (It !=3D End) { + Out << *It; + ++It; + } + while (It !=3D End) { + Out << ", " << *It; + ++It; + } + Out << ");\n"; +} + +template +void emitCallTcg(raw_ostream &Out, const StringRef S, Iterator Begin, + Iterator End) +{ + assert(Begin !=3D End); + Out << S << '('; + Out << *Begin; + ++Begin; + while (Begin !=3D End) { + Out << ", " << *Begin; + ++Begin; + } + Out << ");\n"; +} + +inline void emitArgListTcg(raw_ostream &Out, + const std::initializer_list Args) +{ + emitArgListTcg(Out, Args.begin(), Args.end()); +} + +inline void emitCall(raw_ostream &Out, const StringRef &S, + const std::initializer_list Args) +{ + emitCall(Out, S, Args.begin(), Args.end()); +} + +inline void emitCallTcg(raw_ostream &Out, const StringRef &S, + std::initializer_list Args) +{ + emitCallTcg(Out, S, Args.begin(), Args.end()); +} + +inline void genCallHelper(raw_ostream &Out, const StringRef &Helper, + const std::initializer_list Args) +{ + auto Func =3D Twine("gen_").concat(Helper).str(); + emitCallTcg(Out, Func, Args); +} + +template +void genCallHelper(raw_ostream &Out, const StringRef &Helper, I Beg, I End) +{ + auto Func =3D Twine("gen_").concat(Helper).str(); + emitCallTcg(Out, Func, Beg, End); +} + +void tempNew(raw_ostream &Out, const TcgV &Value); +void tempNewPtr(raw_ostream &Out); +void tempNewVec(raw_ostream &Out); + +void genNewLabel(raw_ostream &Out); +void genSetLabel(raw_ostream &Out, const TcgV &L); + +void defineNewTemp(raw_ostream &Out, const TcgV &Tcg); + +void genBr(raw_ostream &Out, const TcgV &L); + +void genTempInit(raw_ostream &Out, const TcgV &Arg1, const StringRef Str); +void genTempInit(raw_ostream &Out, const TcgV &Arg1, uint64_t Value); +void genTempInit(raw_ostream &Out, const TcgV &Arg1, const TcgV &Arg2); +void genAssignConst(raw_ostream &Out, const TcgV &Arg1, const StringRef St= r); +void genAssignConst(raw_ostream &Out, const TcgV &Arg1, uint64_t Value); +void genAssignConst(raw_ostream &Out, const TcgV &Arg1, const TcgV &Arg2); + +void genExtI32I64(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genExtrlI64I32(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genExtuI32I64(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genExtrhI64I32(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genExtract(raw_ostream &Out, bool Sign, const TcgV &Dst, const TcgV &= Src, + const TcgV &Offset, const TcgV &Length); +void genDeposit(raw_ostream &Out, const TcgV &Dst, const TcgV &Into, + const TcgV &From, const TcgV &Offset, const TcgV &Length); + +void genTruncPtr(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); + +void genConcat(raw_ostream &Out, const TcgV &Dst, const TcgV &Src1, + const TcgV &Src2); +void genMov(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genMovPtr(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genAddPtr(raw_ostream &Out, const TcgV &Dst, const TcgV &Ptr, + const TcgV &Offset); +void genBinOp(raw_ostream &Out, const TcgV &Dst, + const Instruction::BinaryOps Opcode, const TcgV &Src0, + const TcgV &Src1); +void genMovI(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); + +void genMovcond(raw_ostream &Out, const CmpInst::Predicate &Pred, + const TcgV &Ret, const TcgV &C1, const TcgV &C2, const Tcg= V &V1, + const TcgV &V2); +void genSetcond(raw_ostream &Out, const CmpInst::Predicate &Pred, + const TcgV &Dst, const TcgV &Op1, const TcgV &Op2); +void genSetcondI(raw_ostream &Out, const CmpInst::Predicate &Pred, + const TcgV &Dst, const TcgV &Op1, const TcgV &Op2); +void genBrcond(raw_ostream &Out, const CmpInst::Predicate &Pred, + const TcgV &Arg1, const TcgV &Arg2, const TcgV &Label); + +void genQemuLoad(raw_ostream &Out, const TcgV &Dst, const TcgV &Ptr, + const char *MemOpStr); +void genQemuStore(raw_ostream &Out, const TcgV &Ptr, const TcgV &Src, + const char *MemOpStr); + +void genLd(raw_ostream &Out, const TcgV &Dst, const TcgV &Ptr, uint64_t Of= fset); +void genSt(raw_ostream &Out, const TcgV &Ptr, const TcgV &Src, uint64_t Of= fset); + +void genFunnelShl(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1, const TcgV &Shift); +void genBitreverse(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genAbs(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genCountLeadingZeros(raw_ostream &Out, const TcgV &Dst, const TcgV &S= rc); +void genCountTrailingZeros(raw_ostream &Out, const TcgV &Dst, const TcgV &= Src); +void genCountOnes(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genByteswap(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); + +// Vector ops. +void genVecBinOp(raw_ostream &Out, const Instruction::BinaryOps Opcode, + const TcgV &Dst, const TcgV &Src0, const TcgV &Src1); +void genVecSignedSatAdd(raw_ostream &Out, const TcgV &Dst, const TcgV &Src= 0, + const TcgV &Src1); +void genVecSignedSatSub(raw_ostream &Out, const TcgV &Dst, const TcgV &Src= 0, + const TcgV &Src1); +void genVecSignedMax(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1); +void genVecUnsignedMax(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1); +void genVecSignedMin(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1); +void genVecUnsignedMin(raw_ostream &Out, const TcgV &Dst, const TcgV &Src0, + const TcgV &Src1); +void genVecMemcpy(raw_ostream &Out, const TcgV &Dst, const TcgV &Src, + const TcgV &Size); +void genVecMemset(raw_ostream &Out, const TcgV &Dst, const TcgV &Src, + const TcgV &Size); +void genVecSplat(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genVecArrSplat(raw_ostream &Out, const TcgV &Dst, + SmallVector &Arr); +void genVecBitsel(raw_ostream &Out, const TcgV &Dst, const TcgV &Cond, + const TcgV &Src0, const TcgV &Src1); +void genVecCmp(raw_ostream &Out, const TcgV &Dst, + const CmpInst::Predicate &Pred, const TcgV &Src0, + const TcgV &Src1); +void genVecNot(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genVecTrunc(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genVecSext(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); +void genVecZext(raw_ostream &Out, const TcgV &Dst, const TcgV &Src); + +} // namespace tcg + +namespace c +{ + +TcgV ptrAdd(const TcgV &Ptr, const TcgV &Offset); +TcgV ternary(const TcgV &Cond, const TcgV &True, const TcgV &False); +TcgV deref(const TcgV &Ptr, uint32_t LlvmSize, uint32_t TcgSize); +TcgV compare(const CmpInst::Predicate &Pred, const TcgV &Src0, + const TcgV &Src1); +TcgV zext(const TcgV &V, uint32_t LlvmSize, uint32_t TcgSize); +TcgV sext(const TcgV &V, uint32_t LlvmSize, uint32_t TcgSize); +TcgV binop(Instruction::BinaryOps Opcode, const TcgV &Src0, const TcgV &Sr= c1); + +void emitVectorPreamble(raw_ostream &Out); +void emitVectorMemVar(raw_ostream &Out); + +} // namespace c diff --git a/subprojects/helper-to-tcg/pipeline/Pipeline.cpp b/subprojects/= helper-to-tcg/pipeline/Pipeline.cpp index b933a7bb1a..004c16550a 100644 --- a/subprojects/helper-to-tcg/pipeline/Pipeline.cpp +++ b/subprojects/helper-to-tcg/pipeline/Pipeline.cpp @@ -68,6 +68,19 @@ cl::opt cl::desc("Pointer size of the guest architecture"), cl::init(32), cl::cat(Cat)); =20 +// Options for TcgEmit +cl::opt MmuIndexFunction( + "mmu-index-function", + cl::desc("Name of a (uint32_t tb_flag) -> int function returning the " + "mmu index from the tb_flags of the current translation block= "), + cl::init("get_tb_mmu_index"), cl::cat(Cat)); + +cl::opt + TempVectorBlock("temp-vector-block", + cl::desc("Name of uint8_t[...] field in CPUArchState u= sed " + "for allocating temporary gvec variables"), + cl::init("tmp_vmem"), cl::cat(Cat)); + // Define a TargetTransformInfo (TTI) subclass, this allows for overriding // common per-llvm-target information expected by other LLVM passes, such // as the width of the largest scalar/vector registers. Needed for consis= tent --=20 2.45.2