diff --git a/llvm/lib/Target/AVR/AVR.h b/llvm/lib/Target/AVR/AVR.h --- a/llvm/lib/Target/AVR/AVR.h +++ b/llvm/lib/Target/AVR/AVR.h @@ -19,8 +19,11 @@ namespace llvm { +class AVRRegisterBankInfo; +class AVRSubtarget; class AVRTargetMachine; class FunctionPass; +class InstructionSelector; FunctionPass *createAVRISelDag(AVRTargetMachine &TM, CodeGenOpt::Level OptLevel); @@ -33,6 +36,11 @@ void initializeAVRExpandPseudoPass(PassRegistry&); void initializeAVRRelaxMemPass(PassRegistry&); +InstructionSelector *createAVRInstructionSelector(const AVRTargetMachine &, + AVRSubtarget &, + AVRRegisterBankInfo &); +} + /// Contains the AVR backend. namespace AVR { diff --git a/llvm/lib/Target/AVR/AVR.td b/llvm/lib/Target/AVR/AVR.td --- a/llvm/lib/Target/AVR/AVR.td +++ b/llvm/lib/Target/AVR/AVR.td @@ -26,6 +26,12 @@ include "AVRRegisterInfo.td" +//===---------------------------------------------------------------------===// +// Registerbank Descriptions +//===---------------------------------------------------------------------===// + +include "AVRRegisterBanks.td" + //===---------------------------------------------------------------------===// // Instruction Descriptions //===---------------------------------------------------------------------===// diff --git a/llvm/lib/Target/AVR/AVRCallLowering.h b/llvm/lib/Target/AVR/AVRCallLowering.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRCallLowering.h @@ -0,0 +1,42 @@ +//===-- AVRCallLowering.h - Call lowering ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file describes how to lower LLVM calls to machine code calls. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AVR_AVRCALLLOWERING_H +#define LLVM_LIB_TARGET_AVR_AVRCALLLOWERING_H + +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/CodeGen/ValueTypes.h" + +namespace llvm { + +class AVRTargetLowering; + +class AVRCallLowering : public CallLowering { + +public: + AVRCallLowering(const AVRTargetLowering &TLI); + + bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val, + ArrayRef VRegs) const override; + + bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, + ArrayRef> VRegs) const override; + + bool lowerCall(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info) const override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_AVR_AVRCALLLOWERING_H diff --git a/llvm/lib/Target/AVR/AVRCallLowering.cpp b/llvm/lib/Target/AVR/AVRCallLowering.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRCallLowering.cpp @@ -0,0 +1,50 @@ +//===-- AVRCallLowering.cpp - Call lowering -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file implements the lowering of LLVM calls to machine code calls for +/// GlobalISel. +// +//===----------------------------------------------------------------------===// + +#include "AVRCallLowering.h" +#include "AVRISelLowering.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" + +using namespace llvm; + +AVRCallLowering::AVRCallLowering(const AVRTargetLowering &TLI) + : CallLowering(&TLI) {} + +bool AVRCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, + const Value *Val, + ArrayRef VRegs) const { + + MachineInstrBuilder Ret = MIRBuilder.buildInstrNoInsert(AVR::PseudoRET); + + if (Val != nullptr) { + return false; + } + MIRBuilder.insertInstr(Ret); + return true; +} + +bool AVRCallLowering::lowerFormalArguments( + MachineIRBuilder &MIRBuilder, const Function &F, + ArrayRef> VRegs) const { + + if (F.arg_empty()) + return true; + + return false; +} + +bool AVRCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info) const { + return false; +} diff --git a/llvm/lib/Target/AVR/AVRInstructionSelector.cpp b/llvm/lib/Target/AVR/AVRInstructionSelector.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRInstructionSelector.cpp @@ -0,0 +1,101 @@ +//===-- AVRInstructionSelector.cpp -----------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the InstructionSelector class for +/// AVR. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "AVRRegisterBankInfo.h" +#include "AVRSubtarget.h" +#include "AVRTargetMachine.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "avr-isel" + +using namespace llvm; + +#define GET_GLOBALISEL_PREDICATE_BITSET +#include "AVRGenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATE_BITSET + +namespace { + +class AVRInstructionSelector : public InstructionSelector { +public: + AVRInstructionSelector(const AVRTargetMachine &TM, const AVRSubtarget &STI, + const AVRRegisterBankInfo &RBI); + + bool select(MachineInstr &I) override; + static const char *getName() { return DEBUG_TYPE; } + +private: + bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const; + + const AVRSubtarget &STI; + const AVRInstrInfo &TII; + const AVRRegisterInfo &TRI; + const AVRRegisterBankInfo &RBI; + + // FIXME: This is necessary because DAGISel uses "Subtarget->" and GlobalISel + // uses "STI." in the code generated by TableGen. We need to unify the name of + // Subtarget variable. + const AVRSubtarget *Subtarget = &STI; + +#define GET_GLOBALISEL_PREDICATES_DECL +#include "AVRGenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_DECL + +#define GET_GLOBALISEL_TEMPORARIES_DECL +#include "AVRGenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_DECL +}; + +} // end anonymous namespace + +#define GET_GLOBALISEL_IMPL +#include "AVRGenGlobalISel.inc" +#undef GET_GLOBALISEL_IMPL + +AVRInstructionSelector::AVRInstructionSelector(const AVRTargetMachine &TM, + const AVRSubtarget &STI, + const AVRRegisterBankInfo &RBI) + : InstructionSelector(), STI(STI), TII(*STI.getInstrInfo()), + TRI(*STI.getRegisterInfo()), RBI(RBI), + +#define GET_GLOBALISEL_PREDICATES_INIT +#include "AVRGenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_INIT +#define GET_GLOBALISEL_TEMPORARIES_INIT +#include "AVRGenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_INIT +{ +} + +bool AVRInstructionSelector::select(MachineInstr &I) { + + if (!isPreISelGenericOpcode(I.getOpcode())) { + // Certain non-generic instructions also need some special handling. + return true; + } + + if (selectImpl(I, *CoverageInfo)) + return true; + + return false; +} + +namespace llvm { +InstructionSelector *createAVRInstructionSelector(const AVRTargetMachine &TM, + AVRSubtarget &Subtarget, + AVRRegisterBankInfo &RBI) { + return new AVRInstructionSelector(TM, Subtarget, RBI); +} +} // end namespace llvm diff --git a/llvm/lib/Target/AVR/AVRLegalizerInfo.h b/llvm/lib/Target/AVR/AVRLegalizerInfo.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRLegalizerInfo.h @@ -0,0 +1,28 @@ +//===-- AVRLegalizerInfo.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the Machinelegalizer class for AVR. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AVR_AVRMACHINELEGALIZER_H +#define LLVM_LIB_TARGET_AVR_AVRMACHINELEGALIZER_H + +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" + +namespace llvm { + +class AVRSubtarget; + +/// This class provides the information for the target register banks. +class AVRLegalizerInfo : public LegalizerInfo { +public: + AVRLegalizerInfo(const AVRSubtarget &ST); +}; +} // end namespace llvm +#endif diff --git a/llvm/lib/Target/AVR/AVRLegalizerInfo.cpp b/llvm/lib/Target/AVR/AVRLegalizerInfo.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRLegalizerInfo.cpp @@ -0,0 +1,21 @@ +//===-- AVRLegalizerInfo.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the Machinelegalizer class for AVR. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "AVRLegalizerInfo.h" +#include "llvm/CodeGen/TargetOpcodes.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Type.h" + +using namespace llvm; + +AVRLegalizerInfo::AVRLegalizerInfo(const AVRSubtarget &ST) { computeTables(); } diff --git a/llvm/lib/Target/AVR/AVRRegisterBankInfo.h b/llvm/lib/Target/AVR/AVRRegisterBankInfo.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRRegisterBankInfo.h @@ -0,0 +1,37 @@ +//===-- AVRRegisterBankInfo.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the RegisterBankInfo class for AVR. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AVR_AVRREGISTERBANKINFO_H +#define LLVM_LIB_TARGET_AVR_AVRREGISTERBANKINFO_H + +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" + +#define GET_REGBANK_DECLARATIONS +#include "AVRGenRegisterBank.inc" + +namespace llvm { + +class TargetRegisterInfo; + +class AVRGenRegisterBankInfo : public RegisterBankInfo { +protected: +#define GET_TARGET_REGBANK_CLASS +#include "AVRGenRegisterBank.inc" +}; + +/// This class provides the information for the target register banks. +class AVRRegisterBankInfo final : public AVRGenRegisterBankInfo { +public: + AVRRegisterBankInfo(const TargetRegisterInfo &TRI); +}; +} // end namespace llvm +#endif diff --git a/llvm/lib/Target/AVR/AVRRegisterBankInfo.cpp b/llvm/lib/Target/AVR/AVRRegisterBankInfo.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRRegisterBankInfo.cpp @@ -0,0 +1,26 @@ +//===-- AVRRegisterBankInfo.cpp -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the RegisterBankInfo class for AVR. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "AVRRegisterBankInfo.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" +#include "llvm/CodeGen/GlobalISel/RegisterBank.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_TARGET_REGBANK_IMPL +#include "AVRGenRegisterBank.inc" + +using namespace llvm; + +AVRRegisterBankInfo::AVRRegisterBankInfo(const TargetRegisterInfo &TRI) + : AVRGenRegisterBankInfo() {} diff --git a/llvm/lib/Target/AVR/AVRRegisterBanks.td b/llvm/lib/Target/AVR/AVRRegisterBanks.td new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRRegisterBanks.td @@ -0,0 +1,13 @@ +//=-- AVRRegisterBank.td - Describe the AVR Banks --------*- tablegen -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +/// General Purpose Registers: X. +def GPRRegBank : RegisterBank<"GPRB", [GPR]>; diff --git a/llvm/lib/Target/AVR/AVRSubtarget.h b/llvm/lib/Target/AVR/AVRSubtarget.h --- a/llvm/lib/Target/AVR/AVRSubtarget.h +++ b/llvm/lib/Target/AVR/AVRSubtarget.h @@ -13,6 +13,10 @@ #ifndef LLVM_AVR_SUBTARGET_H #define LLVM_AVR_SUBTARGET_H +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/DataLayout.h" #include "llvm/Target/TargetMachine.h" @@ -118,6 +122,19 @@ AVRFrameLowering FrameLowering; AVRTargetLowering TLInfo; AVRSelectionDAGInfo TSInfo; + +protected: + // GlobalISel related APIs. + std::unique_ptr CallLoweringInfo; + std::unique_ptr InstSelector; + std::unique_ptr Legalizer; + std::unique_ptr RegBankInfo; + +public: + const CallLowering *getCallLowering() const override; + InstructionSelector *getInstructionSelector() const override; + const LegalizerInfo *getLegalizerInfo() const override; + const RegisterBankInfo *getRegBankInfo() const override; }; } // end namespace llvm diff --git a/llvm/lib/Target/AVR/AVRSubtarget.cpp b/llvm/lib/Target/AVR/AVRSubtarget.cpp --- a/llvm/lib/Target/AVR/AVRSubtarget.cpp +++ b/llvm/lib/Target/AVR/AVRSubtarget.cpp @@ -16,6 +16,9 @@ #include "llvm/Support/TargetRegistry.h" #include "AVR.h" +#include "AVRCallLowering.h" +#include "AVRLegalizerInfo.h" +#include "AVRRegisterBankInfo.h" #include "AVRTargetMachine.h" #include "MCTargetDesc/AVRMCTargetDesc.h" @@ -44,6 +47,30 @@ TLInfo(TM, initializeSubtargetDependencies(CPU, FS, TM)), TSInfo() { // Parse features string. ParseSubtargetFeatures(CPU, /*TuneCPU*/ CPU, FS); + + CallLoweringInfo.reset(new AVRCallLowering(*getTargetLowering())); + Legalizer.reset(new AVRLegalizerInfo(*this)); + + auto *RBI = new AVRRegisterBankInfo(*getRegisterInfo()); + RegBankInfo.reset(RBI); + InstSelector.reset(createAVRInstructionSelector( + *static_cast(&TM), *this, *RBI)); +} + +const CallLowering *AVRSubtarget::getCallLowering() const { + return CallLoweringInfo.get(); +} + +InstructionSelector *AVRSubtarget::getInstructionSelector() const { + return InstSelector.get(); +} + +const LegalizerInfo *AVRSubtarget::getLegalizerInfo() const { + return Legalizer.get(); +} + +const RegisterBankInfo *AVRSubtarget::getRegBankInfo() const { + return RegBankInfo.get(); } AVRSubtarget & diff --git a/llvm/lib/Target/AVR/AVRTargetMachine.cpp b/llvm/lib/Target/AVR/AVRTargetMachine.cpp --- a/llvm/lib/Target/AVR/AVRTargetMachine.cpp +++ b/llvm/lib/Target/AVR/AVRTargetMachine.cpp @@ -12,6 +12,10 @@ #include "AVRTargetMachine.h" +#include "llvm/CodeGen/GlobalISel/IRTranslator.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" +#include "llvm/CodeGen/GlobalISel/Legalizer.h" +#include "llvm/CodeGen/GlobalISel/RegBankSelect.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/LegacyPassManager.h" @@ -65,10 +69,14 @@ return getTM(); } + bool addGlobalInstructionSelect() override; bool addInstSelector() override; + bool addIRTranslator() override; + bool addLegalizeMachineIR() override; void addPreSched2() override; void addPreEmitPass() override; void addPreRegAlloc() override; + bool addRegBankSelect() override; }; } // namespace @@ -81,6 +89,7 @@ RegisterTargetMachine X(getTheAVRTarget()); auto &PR = *PassRegistry::getPassRegistry(); + initializeGlobalISel(*PR); initializeAVRExpandPseudoPass(PR); initializeAVRRelaxMemPass(PR); } @@ -106,6 +115,26 @@ return false; } +bool AVRPassConfig::addIRTranslator() { + addPass(new IRTranslator()); + return false; +} + +bool AVRPassConfig::addLegalizeMachineIR() { + addPass(new Legalizer()); + return false; +} + +bool AVRPassConfig::addRegBankSelect() { + addPass(new RegBankSelect()); + return false; +} + +bool AVRPassConfig::addGlobalInstructionSelect() { + addPass(new InstructionSelect()); + return false; +} + void AVRPassConfig::addPreRegAlloc() { // Create the dynalloc SP save/restore pass to handle variable sized allocas. addPass(createAVRDynAllocaSRPass()); diff --git a/llvm/lib/Target/AVR/CMakeLists.txt b/llvm/lib/Target/AVR/CMakeLists.txt --- a/llvm/lib/Target/AVR/CMakeLists.txt +++ b/llvm/lib/Target/AVR/CMakeLists.txt @@ -7,8 +7,10 @@ tablegen(LLVM AVRGenCallingConv.inc -gen-callingconv) tablegen(LLVM AVRGenDAGISel.inc -gen-dag-isel) tablegen(LLVM AVRGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM AVRGenGlobalISel.inc -gen-global-isel) tablegen(LLVM AVRGenInstrInfo.inc -gen-instr-info) tablegen(LLVM AVRGenMCCodeEmitter.inc -gen-emitter) +tablegen(LLVM AVRGenRegisterBank.inc -gen-register-bank) tablegen(LLVM AVRGenRegisterInfo.inc -gen-register-info) tablegen(LLVM AVRGenSubtargetInfo.inc -gen-subtarget) @@ -16,13 +18,17 @@ add_llvm_target(AVRCodeGen AVRAsmPrinter.cpp + AVRCallLowering.cpp AVRExpandPseudoInsts.cpp AVRFrameLowering.cpp AVRInstrInfo.cpp + AVRInstructionSelector.cpp AVRISelDAGToDAG.cpp AVRISelLowering.cpp + AVRLegalizerInfo.cpp AVRMCInstLower.cpp AVRRelaxMemOperations.cpp + AVRRegisterBankInfo.cpp AVRRegisterInfo.cpp AVRSubtarget.cpp AVRTargetMachine.cpp @@ -35,6 +41,7 @@ AsmPrinter CodeGen Core + GlobalISel MC AVRDesc AVRInfo diff --git a/llvm/test/CodeGen/AVR/GlobalISel/calllowering-ret.ll b/llvm/test/CodeGen/AVR/GlobalISel/calllowering-ret.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AVR/GlobalISel/calllowering-ret.ll @@ -0,0 +1,11 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=avr -global-isel -verify-machineinstrs < %s \ +; RUN: | FileCheck %s + +define void @foo() { + ; CHECK-LABEL: foo + ; CHECK: # %bb.0: # %entry + ; CHECK: ret +entry: + ret void +} diff --git a/llvm/test/CodeGen/AVR/GlobalISel/irtranslator.ll b/llvm/test/CodeGen/AVR/GlobalISel/irtranslator.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AVR/GlobalISel/irtranslator.ll @@ -0,0 +1,7 @@ +; RUN: llc -mtriple=avr -global-isel -verify-machineinstrs -stop-after=irtranslator < %s | FileCheck %s + +; CHECK: name: f +; CHECK: BLR8 +define void @f() { + ret void +} diff --git a/llvm/test/CodeGen/AVR/GlobalISel/legalize-ret.mir b/llvm/test/CodeGen/AVR/GlobalISel/legalize-ret.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AVR/GlobalISel/legalize-ret.mir @@ -0,0 +1,17 @@ +# RUN: llc -mtriple=avr -global-isel -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s + +--- +name: test_simple +body: | + ; CHECK-LABEL: name: test_simple + ; CHECK: [[IN:%[0-9]+]]:_(s64) = COPY $x3 + ; CHECK: $x3 = COPY [[IN]] + ; CHECK: BLR8 implicit $lr8, implicit $rm, implicit $x3 + bb.1.entry: + liveins: $x3 + + %0:_(s64) = COPY $x3 + $x3 = COPY %0(s64) + BLR8 implicit $lr8, implicit $rm, implicit $x3 + +...