<commit_before>//===--- Editor.cpp - Output Of Text ----------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines the text manipulation ("editing") interface.
//
//  Axel Naumann <axel@cern.ch>, 2011-05-12
//===----------------------------------------------------------------------===//

#include "textinput/Editor.h"

#include "textinput/Callbacks.h"
#include "textinput/History.h"
#include "textinput/KeyBinding.h"
#include "textinput/TextInput.h"
#include "textinput/TextInputContext.h"

#include <algorithm>

namespace textinput {
  Editor::EProcessResult
  Editor::Process(Command cmd, EditorRange& R) {
    switch (cmd.GetKind()) {
      case kCKChar:
        return ProcessChar(cmd.GetChar(), R);
      case kCKMove:
        return ProcessMove(cmd.GetMoveID(), R);
      case kCKCommand:
        return ProcessCommand(cmd.GetCommandID(), R);
      case kCKControl:
      case kCKError:
        return kPRError;
    }
    return kPRError;
  }

  Range
  Editor::ResetText() {
    bool addToHist = !fContext->GetLine().empty()
      && !fContext->GetTextInput()->IsInputHidden()
      && fContext->GetTextInput()->IsAutoHistAddEnabled();
    if (addToHist) {
      fContext->GetHistory()->AddLine(fContext->GetLine().GetText());
      if (fReplayHistEntry != (size_t) -1) {
        // Added a line, thus renumber
        ++fReplayHistEntry;
      }
    }
    Range R(0, fContext->GetLine().length());
    fContext->GetLine().clear();
    fContext->SetCursor(0);
    ClearPasteBuf();
    fSearch.clear();
    CancelSpecialInputMode(R);
    if (fReplayHistEntry != (size_t) -1) {
      --fReplayHistEntry; // intentional overflow to -1
      fContext->SetLine(fContext->GetHistory()->GetLine(fReplayHistEntry));
    }
    return R;
  }

  void
  Editor::SetReverseHistSearchPrompt(Range& RDisplay) {
    std::string P("[bkw'");
    SetEditorPrompt(Text(P + fSearch + "'] "));
    RDisplay.ExtendPromptUpdate(Range::kUpdateEditorPrompt);
  }

  bool
  Editor::UpdateHistSearch(EditorRange& R) {
    History* Hist = fContext->GetHistory();
    Text& Line = fContext->GetLine();
    size_t NewHistEntry = (size_t) -1;
    if (fSearch.empty()) {
      NewHistEntry = 0;
    } else {
      size_t startAt = fCurHistEntry;
      if (startAt == (size_t) -1) {
        startAt = 0;
      }
      for (size_t i = startAt, n = Hist->GetSize(); i < n; ++i) {
        if (Hist->GetLine(i).find(fSearch) != std::string::npos) {
          NewHistEntry = i;
          break;
        }
      }
    }
    if (NewHistEntry != (size_t) -1) {
      if (NewHistEntry != fCurHistEntry) {
        fCurHistEntry = NewHistEntry;
        Line = Hist->GetLine(fCurHistEntry);
        R.fEdit.Extend(Range::AllText());
        R.fDisplay.Extend(Range::AllText());
        // Resets mode, thus can't call:
        // ProcessMove(kMoveEnd, R);
        fContext->SetCursor(Line.length());
      }
      return true;
    }

    fCurHistEntry = (size_t) -1;
    return false;
  }

  void
  Editor::CancelSpecialInputMode(Range& DisplayR) {
    if (fMode == kInputMode) return;
    fContext->GetKeyBinding()->SetAllowEscModifier(false);
    SetEditorPrompt(Text());
    DisplayR.ExtendPromptUpdate(Range::kUpdateEditorPrompt);
    fMode = kInputMode;
  }

  Editor::EProcessResult
  Editor::ProcessChar(char C, EditorRange& R) {
    if (C < 32) return kPRError;

    if (fMode == kHistSearchMode) {
      fSearch += C;
      SetReverseHistSearchPrompt(R.fDisplay);
      if (UpdateHistSearch(R)) return kPRSuccess;
      return kPRError;
    }

    PushUndo();
    ClearPasteBuf();

    Text& Line = fContext->GetLine();
    size_t Cursor = fContext->GetCursor();

    if (fOverwrite) {
      if (Cursor < Line.length()) {
        Line[Cursor] = C;
      } else {
        Line += C;
      }
      R.fEdit.Extend(Range(Cursor));
      R.fDisplay.Extend(Range(Cursor));
    } else {
      Line.insert(Cursor, C);
      R.fEdit.Extend(Range(Cursor));
      R.fDisplay.Extend(Range(Cursor, Range::End()));
      fContext->SetCursor(Cursor + 1);
    }
    return kPRSuccess;
  }

  Editor::EProcessResult
  Editor::ProcessMove(EMoveID M, EditorRange &R) {
    ClearPasteBuf();
    CancelSpecialInputMode(R.fDisplay);

    size_t Cursor = fContext->GetCursor();
    size_t LineLen = fContext->GetLine().length();

    switch (M) {
      case kMoveEnd: fContext->SetCursor(LineLen); return kPRSuccess;
      case kMoveFront: fContext->SetCursor(0); return kPRSuccess;
      case kMoveRight:
        if (Cursor < LineLen) {
          fContext->SetCursor(Cursor + 1);
          return kPRSuccess;
        } else {
          return kPRError;
        }
      case kMoveLeft:
        if (Cursor > 0) {
          fContext->SetCursor(Cursor - 1);
          return kPRSuccess;
        } else {
          return kPRError;
        }
      case kMoveNextWord:
      case kMovePrevWord:
        fContext->SetCursor(FindWordBoundary(M == kMoveNextWord ? 1 : -1));
        return kPRSuccess;
    }
    return kPRError;
  }

  Editor::EProcessResult
  Editor::ProcessCommand(ECommandID M, EditorRange &R) {
    if (M < kCmd_END_TEXT_MODIFYING_CMDS) {
      PushUndo();
    }
    if (fMode == kHistSearchMode) {
      if (M == kCmdDelLeft) {
        if (fSearch.empty()) return kPRError;
        fSearch.erase(fSearch.length() - 1);
        SetReverseHistSearchPrompt(R.fDisplay);
        if (UpdateHistSearch(R)) return kPRSuccess;
        return kPRError;
      } else {
        CancelSpecialInputMode(R.fDisplay);
        return kPRError;
      }
    }

    Text& Line = fContext->GetLine();
    size_t Cursor = fContext->GetCursor();
    History* Hist = fContext->GetHistory();

    switch (M) {
      case kCmdIgnore:
        return kPRSuccess;
      case kCmdEnter:
        fReplayHistEntry = (size_t) -1;
        fCurHistEntry = (size_t) -1;
        CancelSpecialInputMode(R.fDisplay);
        return kPRSuccess;
      case kCmdDelLeft:
        if (Cursor == 0) return kPRError;
        fContext->SetCursor(--Cursor);
        // intentional fallthrough:
      case kCmdDel:
        if (Cursor == Line.length()) return kPRError;
        AddToPasteBuf(M == kCmdDel ? 1 : -1, Line[Cursor]);
        Line.erase(Cursor);
        R.fEdit.Extend(Range(Cursor));
        R.fDisplay.Extend(Range(Cursor, Range::End()));
        return kPRSuccess;
      case kCmdCutToEnd:
        AddToPasteBuf(1, Line.GetText().c_str() + Cursor);
        Line.erase(Cursor, Line.length() - Cursor);
        R.fEdit.Extend(Range(Cursor));
        R.fDisplay.Extend(Range(Cursor, Range::End()));
        return kPRSuccess;
      case kCmdCutNextWord:
      {
        size_t posWord = FindWordBoundary(1);
        AddToPasteBuf(1, Line.GetText().substr(Cursor, posWord - Cursor));
        R.fEdit.Extend(Range(Cursor, posWord));
        R.fDisplay.Extend(Range(Cursor, Range::End()));
        Line.erase(Cursor, posWord - Cursor);
        return kPRSuccess;
      }
      case kCmdCutPrevWord:
      {
        size_t posWord = FindWordBoundary(-1);
        AddToPasteBuf(-1, Line.GetText().substr(posWord, Cursor - posWord));
        R.fEdit.Extend(Range(posWord, Cursor));
        R.fDisplay.Extend(Range(posWord, Range::End()));
        Line.erase(posWord, Cursor - posWord);
        fContext->SetCursor(posWord);
        return kPRSuccess;
      }
      case kCmdToggleOverwriteMode:
        fOverwrite = !fOverwrite;
        return kPRSuccess;
      case kCmdInsertMode:
        fOverwrite = false;
        return kPRSuccess;
      case kCmdOverwiteMode:
        fOverwrite = true;
        return kPRSuccess;
      case kCmdCutToFront:
        R.fEdit.Extend(Range(0, Cursor));
        R.fDisplay.Extend(Range::AllText());
        AddToPasteBuf(-1, Line.GetText().substr(0, Cursor));
        Line.erase(0, Cursor);
        fContext->SetCursor(0);
        return kPRSuccess;
      case kCmdPaste:
      {
        size_t PasteLen = fPasteBuf.length();
        R.fEdit.Extend(Range(Cursor, PasteLen));
        R.fDisplay.Extend(Range(Cursor, Range::End()));
        Line.insert(Cursor, fPasteBuf);
        fContext->SetCursor(Cursor + PasteLen);
        ClearPasteBuf();
        return kPRSuccess;
      }
      case kCmdSwapThisAndLeftThenMoveRight:
      {
        if (Cursor < 1) return kPRError;
        R.fEdit.Extend(Range(Cursor - 1, Cursor));
        R.fDisplay.Extend(Range(Cursor - 1, Cursor));
        char tmp = Line.GetText()[Cursor];
        Line[Cursor] = Line[Cursor - 1];
        Line[Cursor] = tmp;
        // optional:
        ProcessMove(kMoveRight, R);
        return kPRSuccess;
      }
      case kCmdToUpperMoveNextWord:
      {
        if (Cursor >= Line.length()) return kPRError;
        Line[Cursor] = toupper(Line[Cursor]);
        R.fEdit.Extend(Range(Cursor));
        R.fDisplay.Extend(Range(Cursor));
        ProcessMove(kMoveNextWord, R);
        return kPRSuccess;
      }
      case kCmdWordToLower:
      case kCmdWordToUpper:
      {
        size_t posWord = FindWordBoundary(1);
        if (M == kCmdWordToUpper) {
          for (size_t i = Cursor; i < posWord; ++i) {
            Line[Cursor] =  toupper(Line[Cursor]);
          }
        } else {
          for (size_t i = Cursor; i < posWord; ++i) {
            Line[Cursor] =  tolower(Line[Cursor]);
          }
        }
        R.fEdit.Extend(Range(Cursor, posWord - Cursor));
        R.fDisplay.Extend(Range(Cursor, posWord - Cursor));
        fContext->SetCursor(posWord);
        return kPRSuccess;
      }
      case kCmdUndo:
        Line = fUndoBuf.back().first;
        fContext->SetCursor(fUndoBuf.back().second);
        fUndoBuf.pop_back();
        return kPRSuccess;
      case kCmdHistNewer:
        // already newest?
        if (fCurHistEntry == (size_t)-1) {
          // not a history line ("newer" doesn't mean anything)?
          return kPRError;
        }
        if (fCurHistEntry == 0) {
          Hist->ModifyLine(fCurHistEntry, Line.GetText().c_str());
          Line = fLineNotInHist;
          fLineNotInHist.clear();
          fCurHistEntry = (size_t)-1; // not in hist
        } else {
          --fCurHistEntry;
          Line = Hist->GetLine(fCurHistEntry);
        }
        R.fEdit.Extend(Range::AllText());
        R.fDisplay.Extend(Range::AllText());
        ProcessMove(kMoveEnd, R);
        return kPRSuccess;
      case kCmdHistOlder:
        // intentional overflow from -1 to 0:
        if (fCurHistEntry + 1 >= Hist->GetSize()) {
          return kPRError;
        }
        if (fCurHistEntry == (size_t)-1) {
          fLineNotInHist = Line.GetText();
          fCurHistEntry = 0;
        } else {
          Hist->ModifyLine(fCurHistEntry, Line.GetText().c_str());
          ++fCurHistEntry;
        }
        Line = Hist->GetLine(fCurHistEntry);
        R.fEdit.Extend(Range::AllText());
        R.fDisplay.Extend(Range::AllText());
        ProcessMove(kMoveEnd, R);
        return kPRSuccess;
      case kCmdReverseSearch:
        fMode = kHistSearchMode;
        fSearch.clear();
        SetReverseHistSearchPrompt(R.fDisplay);
        fContext->GetKeyBinding()->SetAllowEscModifier(true);
        if (UpdateHistSearch(R)) return kPRSuccess;
        return kPRError;
      case kCmdHistReplay:
        if (fCurHistEntry == (size_t) -1) return kPRError;
        fReplayHistEntry = fCurHistEntry;
        return kPRSuccess;
      case kCmdClearScreen:
      case kCmd_END_TEXT_MODIFYING_CMDS:
        return kPRError;
      case kCmdEsc:
        // Already done for all commands:
        //CancelSpecialInputMode(R);
        return kPRSuccess;
      case kCmdComplete:
      {
        // Completion happens below current input.
        ProcessMove(kMoveEnd, R);
        std::vector<std::string> completions;
        TabCompletion* tc = fContext->GetCompletion();
        if (tc) {
          bool ret = tc->Complete(Line, Cursor, R, completions);
          if (ret) {
            if (!completions.empty()) {
              fContext->GetTextInput()->DisplayInfo(completions);
              R.fDisplay.Extend(Range::AllWithPrompt());
            }
            fContext->SetCursor(Cursor);
            return kPRSuccess;
          }
        }
        return kPRError;
      }
      case kCmdWindowResize:
        fContext->GetTextInput()->HandleResize();
        return kPRSuccess;
      case kCmdHistComplete:
        // Not handled yet, todo.
        return kPRError;
    }
    return kPRError;
  }

  size_t
  Editor::FindWordBoundary(int Direction) {
    static const char space[] = " \x9";

    const Text& Line = fContext->GetLine();
    size_t Cursor = fContext->GetCursor();

    if (Direction < 0 && Cursor < 2) return 0;
    size_t ret = Direction > 0 ?
      Line.GetText().find_first_of(space, Cursor + 1)
    : Line.GetText().find_last_of(space, Cursor - 2);

    if (ret == std::string::npos) {
      if (Direction > 0) return Line.length();
      else return 0;
    }

    if (Direction < 0)
      ret += 1;

    if (ret == std::string::npos) {
      if (Direction > 0) return Line.length();
      else return 0;
    }
    return ret;
  }

  void
  Editor::AddToPasteBuf(int Dir, std::string const &T) {
    if (fCutDirection == Dir) {
      if (Dir < 0) {
        fPasteBuf = T + fPasteBuf;
      } else {
        fPasteBuf += T;
      }
    } else {
      fCutDirection = Dir;
      fPasteBuf = T;
    }
  }

  void
  Editor::AddToPasteBuf(int Dir, char T) {
    if (fCutDirection == Dir) {
      if (Dir < 0) {
        fPasteBuf = std::string(1, T) + fPasteBuf;
      } else {
        fPasteBuf += T;
      }
    } else {
      fCutDirection = Dir;
      fPasteBuf = T;
    }
  }

  void
  Editor::PushUndo() {
    static const size_t MaxUndoBufSize = 100;
    if (fUndoBuf.size() > MaxUndoBufSize) {
      fUndoBuf.pop_front();
    }
    fUndoBuf.push_back(std::make_pair(fContext->GetLine(),
                                      fContext->GetCursor()));
  }

   // Pin vtables:
   TabCompletion::~TabCompletion() {}
   FunKey::~FunKey() {}
}
<commit_msg>Fix repeated ^R.<commit_after>//===--- Editor.cpp - Output Of Text ----------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines the text manipulation ("editing") interface.
//
//  Axel Naumann <axel@cern.ch>, 2011-05-12
//===----------------------------------------------------------------------===//

#include "textinput/Editor.h"

#include "textinput/Callbacks.h"
#include "textinput/History.h"
#include "textinput/KeyBinding.h"
#include "textinput/TextInput.h"
#include "textinput/TextInputContext.h"

#include <algorithm>

namespace textinput {
  Editor::EProcessResult
  Editor::Process(Command cmd, EditorRange& R) {
    switch (cmd.GetKind()) {
      case kCKChar:
        return ProcessChar(cmd.GetChar(), R);
      case kCKMove:
        return ProcessMove(cmd.GetMoveID(), R);
      case kCKCommand:
        return ProcessCommand(cmd.GetCommandID(), R);
      case kCKControl:
      case kCKError:
        return kPRError;
    }
    return kPRError;
  }

  Range
  Editor::ResetText() {
    bool addToHist = !fContext->GetLine().empty()
      && !fContext->GetTextInput()->IsInputHidden()
      && fContext->GetTextInput()->IsAutoHistAddEnabled();
    if (addToHist) {
      fContext->GetHistory()->AddLine(fContext->GetLine().GetText());
      if (fReplayHistEntry != (size_t) -1) {
        // Added a line, thus renumber
        ++fReplayHistEntry;
      }
    }
    Range R(0, fContext->GetLine().length());
    fContext->GetLine().clear();
    fContext->SetCursor(0);
    ClearPasteBuf();
    fSearch.clear();
    CancelSpecialInputMode(R);
    if (fReplayHistEntry != (size_t) -1) {
      --fReplayHistEntry; // intentional overflow to -1
      fContext->SetLine(fContext->GetHistory()->GetLine(fReplayHistEntry));
    }
    return R;
  }

  void
  Editor::SetReverseHistSearchPrompt(Range& RDisplay) {
    std::string P("[bkw'");
    SetEditorPrompt(Text(P + fSearch + "'] "));
    RDisplay.ExtendPromptUpdate(Range::kUpdateEditorPrompt);
  }

  bool
  Editor::UpdateHistSearch(EditorRange& R) {
    History* Hist = fContext->GetHistory();
    Text& Line = fContext->GetLine();
    size_t NewHistEntry = (size_t) -1;
    if (fSearch.empty()) {
      NewHistEntry = 0;
    } else {
      size_t startAt = fCurHistEntry;
      if (startAt == (size_t) -1) {
        startAt = 0;
      }
      for (size_t i = startAt, n = Hist->GetSize(); i < n; ++i) {
        if (Hist->GetLine(i).find(fSearch) != std::string::npos) {
          NewHistEntry = i;
          break;
        }
      }
    }
    if (NewHistEntry != (size_t) -1) {
      // No, even if they are unchanged: we might have
      // subsequent ^R updates triggered by faking a different
      // fCurHistEntry.
      // if (NewHistEntry != fCurHistEntry) {
      fCurHistEntry = NewHistEntry;
      Line = Hist->GetLine(fCurHistEntry);
      R.fEdit.Extend(Range::AllText());
      R.fDisplay.Extend(Range::AllText());
      // Resets mode, thus can't call:
      // ProcessMove(kMoveEnd, R);
      fContext->SetCursor(Line.length());
      return true;
    }

    fCurHistEntry = (size_t) -1;
    return false;
  }

  void
  Editor::CancelSpecialInputMode(Range& DisplayR) {
    if (fMode == kInputMode) return;
    fContext->GetKeyBinding()->SetAllowEscModifier(false);
    SetEditorPrompt(Text());
    DisplayR.ExtendPromptUpdate(Range::kUpdateEditorPrompt);
    fMode = kInputMode;
  }

  Editor::EProcessResult
  Editor::ProcessChar(char C, EditorRange& R) {
    if (C < 32) return kPRError;

    if (fMode == kHistSearchMode) {
      fSearch += C;
      SetReverseHistSearchPrompt(R.fDisplay);
      if (UpdateHistSearch(R)) return kPRSuccess;
      return kPRError;
    }

    PushUndo();
    ClearPasteBuf();

    Text& Line = fContext->GetLine();
    size_t Cursor = fContext->GetCursor();

    if (fOverwrite) {
      if (Cursor < Line.length()) {
        Line[Cursor] = C;
      } else {
        Line += C;
      }
      R.fEdit.Extend(Range(Cursor));
      R.fDisplay.Extend(Range(Cursor));
    } else {
      Line.insert(Cursor, C);
      R.fEdit.Extend(Range(Cursor));
      R.fDisplay.Extend(Range(Cursor, Range::End()));
      fContext->SetCursor(Cursor + 1);
    }
    return kPRSuccess;
  }

  Editor::EProcessResult
  Editor::ProcessMove(EMoveID M, EditorRange &R) {
    ClearPasteBuf();
    CancelSpecialInputMode(R.fDisplay);

    size_t Cursor = fContext->GetCursor();
    size_t LineLen = fContext->GetLine().length();

    switch (M) {
      case kMoveEnd: fContext->SetCursor(LineLen); return kPRSuccess;
      case kMoveFront: fContext->SetCursor(0); return kPRSuccess;
      case kMoveRight:
        if (Cursor < LineLen) {
          fContext->SetCursor(Cursor + 1);
          return kPRSuccess;
        } else {
          return kPRError;
        }
      case kMoveLeft:
        if (Cursor > 0) {
          fContext->SetCursor(Cursor - 1);
          return kPRSuccess;
        } else {
          return kPRError;
        }
      case kMoveNextWord:
      case kMovePrevWord:
        fContext->SetCursor(FindWordBoundary(M == kMoveNextWord ? 1 : -1));
        return kPRSuccess;
    }
    return kPRError;
  }

  Editor::EProcessResult
  Editor::ProcessCommand(ECommandID M, EditorRange &R) {
    if (M < kCmd_END_TEXT_MODIFYING_CMDS) {
      PushUndo();
    }
    if (fMode == kHistSearchMode) {
      if (M == kCmdDelLeft) {
        if (fSearch.empty()) return kPRError;
        fSearch.erase(fSearch.length() - 1);
        SetReverseHistSearchPrompt(R.fDisplay);
        if (UpdateHistSearch(R)) return kPRSuccess;
        return kPRError;
      } else if (M == kCmdReverseSearch) {
        // Search again. Move to older hist entry:
        size_t prevHistEntry = fCurHistEntry;
        // intentional overflow from -1 to 0:
        if (fCurHistEntry + 1 >= fContext->GetHistory()->GetSize()) {
          return kPRError;
        }
        if (fCurHistEntry == (size_t)-1) {
          fCurHistEntry = 0;
        } else {
          ++fCurHistEntry;
        }
        if (UpdateHistSearch(R)) return kPRSuccess;
        fCurHistEntry = prevHistEntry;
        return kPRError;
      } else {
        CancelSpecialInputMode(R.fDisplay);
        return kPRError;
      }
    }

    Text& Line = fContext->GetLine();
    size_t Cursor = fContext->GetCursor();
    History* Hist = fContext->GetHistory();

    switch (M) {
      case kCmdIgnore:
        return kPRSuccess;
      case kCmdEnter:
        fReplayHistEntry = (size_t) -1;
        fCurHistEntry = (size_t) -1;
        CancelSpecialInputMode(R.fDisplay);
        return kPRSuccess;
      case kCmdDelLeft:
        if (Cursor == 0) return kPRError;
        fContext->SetCursor(--Cursor);
        // intentional fallthrough:
      case kCmdDel:
        if (Cursor == Line.length()) return kPRError;
        AddToPasteBuf(M == kCmdDel ? 1 : -1, Line[Cursor]);
        Line.erase(Cursor);
        R.fEdit.Extend(Range(Cursor));
        R.fDisplay.Extend(Range(Cursor, Range::End()));
        return kPRSuccess;
      case kCmdCutToEnd:
        AddToPasteBuf(1, Line.GetText().c_str() + Cursor);
        Line.erase(Cursor, Line.length() - Cursor);
        R.fEdit.Extend(Range(Cursor));
        R.fDisplay.Extend(Range(Cursor, Range::End()));
        return kPRSuccess;
      case kCmdCutNextWord:
      {
        size_t posWord = FindWordBoundary(1);
        AddToPasteBuf(1, Line.GetText().substr(Cursor, posWord - Cursor));
        R.fEdit.Extend(Range(Cursor, posWord));
        R.fDisplay.Extend(Range(Cursor, Range::End()));
        Line.erase(Cursor, posWord - Cursor);
        return kPRSuccess;
      }
      case kCmdCutPrevWord:
      {
        size_t posWord = FindWordBoundary(-1);
        AddToPasteBuf(-1, Line.GetText().substr(posWord, Cursor - posWord));
        R.fEdit.Extend(Range(posWord, Cursor));
        R.fDisplay.Extend(Range(posWord, Range::End()));
        Line.erase(posWord, Cursor - posWord);
        fContext->SetCursor(posWord);
        return kPRSuccess;
      }
      case kCmdToggleOverwriteMode:
        fOverwrite = !fOverwrite;
        return kPRSuccess;
      case kCmdInsertMode:
        fOverwrite = false;
        return kPRSuccess;
      case kCmdOverwiteMode:
        fOverwrite = true;
        return kPRSuccess;
      case kCmdCutToFront:
        R.fEdit.Extend(Range(0, Cursor));
        R.fDisplay.Extend(Range::AllText());
        AddToPasteBuf(-1, Line.GetText().substr(0, Cursor));
        Line.erase(0, Cursor);
        fContext->SetCursor(0);
        return kPRSuccess;
      case kCmdPaste:
      {
        size_t PasteLen = fPasteBuf.length();
        R.fEdit.Extend(Range(Cursor, PasteLen));
        R.fDisplay.Extend(Range(Cursor, Range::End()));
        Line.insert(Cursor, fPasteBuf);
        fContext->SetCursor(Cursor + PasteLen);
        ClearPasteBuf();
        return kPRSuccess;
      }
      case kCmdSwapThisAndLeftThenMoveRight:
      {
        if (Cursor < 1) return kPRError;
        R.fEdit.Extend(Range(Cursor - 1, Cursor));
        R.fDisplay.Extend(Range(Cursor - 1, Cursor));
        char tmp = Line.GetText()[Cursor];
        Line[Cursor] = Line[Cursor - 1];
        Line[Cursor] = tmp;
        // optional:
        ProcessMove(kMoveRight, R);
        return kPRSuccess;
      }
      case kCmdToUpperMoveNextWord:
      {
        if (Cursor >= Line.length()) return kPRError;
        Line[Cursor] = toupper(Line[Cursor]);
        R.fEdit.Extend(Range(Cursor));
        R.fDisplay.Extend(Range(Cursor));
        ProcessMove(kMoveNextWord, R);
        return kPRSuccess;
      }
      case kCmdWordToLower:
      case kCmdWordToUpper:
      {
        size_t posWord = FindWordBoundary(1);
        if (M == kCmdWordToUpper) {
          for (size_t i = Cursor; i < posWord; ++i) {
            Line[Cursor] =  toupper(Line[Cursor]);
          }
        } else {
          for (size_t i = Cursor; i < posWord; ++i) {
            Line[Cursor] =  tolower(Line[Cursor]);
          }
        }
        R.fEdit.Extend(Range(Cursor, posWord - Cursor));
        R.fDisplay.Extend(Range(Cursor, posWord - Cursor));
        fContext->SetCursor(posWord);
        return kPRSuccess;
      }
      case kCmdUndo:
        Line = fUndoBuf.back().first;
        fContext->SetCursor(fUndoBuf.back().second);
        fUndoBuf.pop_back();
        return kPRSuccess;
      case kCmdHistNewer:
        // already newest?
        if (fCurHistEntry == (size_t)-1) {
          // not a history line ("newer" doesn't mean anything)?
          return kPRError;
        }
        if (fCurHistEntry == 0) {
          Hist->ModifyLine(fCurHistEntry, Line.GetText().c_str());
          Line = fLineNotInHist;
          fLineNotInHist.clear();
          fCurHistEntry = (size_t)-1; // not in hist
        } else {
          --fCurHistEntry;
          Line = Hist->GetLine(fCurHistEntry);
        }
        R.fEdit.Extend(Range::AllText());
        R.fDisplay.Extend(Range::AllText());
        ProcessMove(kMoveEnd, R);
        return kPRSuccess;
      case kCmdHistOlder:
        // intentional overflow from -1 to 0:
        if (fCurHistEntry + 1 >= Hist->GetSize()) {
          return kPRError;
        }
        if (fCurHistEntry == (size_t)-1) {
          fLineNotInHist = Line.GetText();
          fCurHistEntry = 0;
        } else {
          Hist->ModifyLine(fCurHistEntry, Line.GetText().c_str());
          ++fCurHistEntry;
        }
        Line = Hist->GetLine(fCurHistEntry);
        R.fEdit.Extend(Range::AllText());
        R.fDisplay.Extend(Range::AllText());
        ProcessMove(kMoveEnd, R);
        return kPRSuccess;
      case kCmdReverseSearch:
        fMode = kHistSearchMode;
        fSearch.clear();
        SetReverseHistSearchPrompt(R.fDisplay);
        fContext->GetKeyBinding()->SetAllowEscModifier(true);
        if (UpdateHistSearch(R)) return kPRSuccess;
        return kPRError;
      case kCmdHistReplay:
        if (fCurHistEntry == (size_t) -1) return kPRError;
        fReplayHistEntry = fCurHistEntry;
        return kPRSuccess;
      case kCmdClearScreen:
      case kCmd_END_TEXT_MODIFYING_CMDS:
        return kPRError;
      case kCmdEsc:
        // Already done for all commands:
        //CancelSpecialInputMode(R);
        return kPRSuccess;
      case kCmdComplete:
      {
        // Completion happens below current input.
        ProcessMove(kMoveEnd, R);
        std::vector<std::string> completions;
        TabCompletion* tc = fContext->GetCompletion();
        if (tc) {
          bool ret = tc->Complete(Line, Cursor, R, completions);
          if (ret) {
            if (!completions.empty()) {
              fContext->GetTextInput()->DisplayInfo(completions);
              R.fDisplay.Extend(Range::AllWithPrompt());
            }
            fContext->SetCursor(Cursor);
            return kPRSuccess;
          }
        }
        return kPRError;
      }
      case kCmdWindowResize:
        fContext->GetTextInput()->HandleResize();
        return kPRSuccess;
      case kCmdHistComplete:
        // Not handled yet, todo.
        return kPRError;
    }
    return kPRError;
  }

  size_t
  Editor::FindWordBoundary(int Direction) {
    static const char space[] = " \x9";

    const Text& Line = fContext->GetLine();
    size_t Cursor = fContext->GetCursor();

    if (Direction < 0 && Cursor < 2) return 0;
    size_t ret = Direction > 0 ?
      Line.GetText().find_first_of(space, Cursor + 1)
    : Line.GetText().find_last_of(space, Cursor - 2);

    if (ret == std::string::npos) {
      if (Direction > 0) return Line.length();
      else return 0;
    }

    if (Direction < 0)
      ret += 1;

    if (ret == std::string::npos) {
      if (Direction > 0) return Line.length();
      else return 0;
    }
    return ret;
  }

  void
  Editor::AddToPasteBuf(int Dir, std::string const &T) {
    if (fCutDirection == Dir) {
      if (Dir < 0) {
        fPasteBuf = T + fPasteBuf;
      } else {
        fPasteBuf += T;
      }
    } else {
      fCutDirection = Dir;
      fPasteBuf = T;
    }
  }

  void
  Editor::AddToPasteBuf(int Dir, char T) {
    if (fCutDirection == Dir) {
      if (Dir < 0) {
        fPasteBuf = std::string(1, T) + fPasteBuf;
      } else {
        fPasteBuf += T;
      }
    } else {
      fCutDirection = Dir;
      fPasteBuf = T;
    }
  }

  void
  Editor::PushUndo() {
    static const size_t MaxUndoBufSize = 100;
    if (fUndoBuf.size() > MaxUndoBufSize) {
      fUndoBuf.pop_front();
    }
    fUndoBuf.push_back(std::make_pair(fContext->GetLine(),
                                      fContext->GetCursor()));
  }

   // Pin vtables:
   TabCompletion::~TabCompletion() {}
   FunKey::~FunKey() {}
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: autoregisterhelper.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: kz $ $Date: 2003-11-18 16:34:20 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include "autoregisterhelper.hxx"

#include <rtl/ustring.hxx>

#include <cppunit/autoregister/registerfunc.h>
#include <cppunit/autoregister/callbackstructure.h>
#include <cppunit/result/callbackfunc.h>
#include <cppunit/autoregister/testfunc.h>
#include "filehelper.hxx"

FunctionList m_Functions;
// osl::Mutex   m_Mutex;

void SAL_CALL registerFunc(FktPtr _pFunc, const char* _sFuncName)
{
    // printf("Function register call for func(%s) successful.\n", _sFuncName);
    m_Functions.push_back(_pFunc);
}

// -----------------------------------------------------------------------------
AutomaticRegisterHelper::AutomaticRegisterHelper(rtl::OUString const& _sDLLName, GetOpt & _aOptions /*, JobList * _pJobList*/)
        :DynamicLibraryHelper(_sDLLName, _aOptions)
{
    // try to get the entry pointer
    FktRegAllPtr pFunc = (FktRegAllPtr) m_pModule->getSymbol( rtl::OUString::createFromAscii( "registerAllTestFunction" ) );

    if (pFunc)
    {
        // FktRegFuncPtr pRegisterFunc = &DynamicLibraryHelper::registerFunc;
        // pFunc(pRegisterFunc);
        // osl::Guard aGuard(m_Mutex);
        FktRegFuncPtr pRegisterFunc = &registerFunc;

        CallbackStructure aCallback;
        aCallback.aRegisterFunction = pRegisterFunc;

        aCallback.aCallbackDispatch  = &CallbackDispatch;

        // special parameter for API testing
        if (_aOptions.hasOpt("-forward"))
        {
            aCallback.psForward = _aOptions.getOpt("-forward").getStr();
        }

        // aCallback.pJobList = _pJobList;

        //# aCallback.aStartTest  = &TestResult_startTest;
        //# aCallback.aAddFailure = &TestResult_addFailure;
        //# aCallback.aAddError   = &TestResult_addError;
        //# aCallback.aEndTest    = &TestResult_endTest;
        //# aCallback.aShouldStop = &TestResult_shouldStop;
        //# aCallback.aAddInfo    = &TestResult_addInfo;
        //# aCallback.aEnterNode  = &TestResult_enterNode;
        //# aCallback.aLeaveNode  = &TestResult_leaveNode;

        aCallback.nBits       = FileHelper::createFlags(_aOptions);

        pFunc(&aCallback);

        if (aCallback.nMagic == aCallback.nMagic2)
        {
            // ok internal simple test done.
            m_aFunctionList = m_Functions;
        }
        else
        {
            // ERROR, the function seams not to be what we thing it's to be.
            fprintf(stderr, "error: Internal check failed. Structure inconsistent, Value Magic2 != Magic.\nPlease recompile your test libraries.");
            exit(-1);
        }
    }
    else
    {
        fprintf(stderr, "warning: Function 'registerAllTestFunction' not found.\n");
        fprintf(stderr, "If you think, you are right, build testshl2 completly new.\n");
    }
}

void AutomaticRegisterHelper::CallAll(hTestResult _hResult) const
{
    for (FunctionList::const_iterator it = m_aFunctionList.begin();
         it != m_aFunctionList.end();
         ++it)
    {
        FktPtr pFunc = *it;
        if (pFunc)
        {
            (pFunc)(_hResult);
        }
    }
}

<commit_msg>INTEGRATION: CWS qadev19 (1.5.36); FILE MERGED 2004/09/22 08:12:35 lla 1.5.36.1: #i34302# testshl2 will fail, if library can't load<commit_after>/*************************************************************************
 *
 *  $RCSfile: autoregisterhelper.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: pjunck $ $Date: 2004-11-02 10:25:13 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include "autoregisterhelper.hxx"

#include <rtl/ustring.hxx>

#include <cppunit/autoregister/registerfunc.h>
#include <cppunit/autoregister/callbackstructure.h>
#include <cppunit/result/callbackfunc.h>
#include <cppunit/autoregister/testfunc.h>
#include "filehelper.hxx"

FunctionList m_Functions;
// osl::Mutex   m_Mutex;

void SAL_CALL registerFunc(FktPtr _pFunc, const char* _sFuncName)
{
    // printf("Function register call for func(%s) successful.\n", _sFuncName);
    m_Functions.push_back(_pFunc);
}

// -----------------------------------------------------------------------------
AutomaticRegisterHelper::AutomaticRegisterHelper(rtl::OUString const& _sDLLName, GetOpt & _aOptions /*, JobList * _pJobList*/)
        :DynamicLibraryHelper(_sDLLName, _aOptions),
         m_bLoadLibraryOK(false)
{
    // try to get the entry pointer
    FktRegAllPtr pFunc = (FktRegAllPtr) m_pModule->getSymbol( rtl::OUString::createFromAscii( "registerAllTestFunction" ) );

    if (pFunc)
    {
        m_bLoadLibraryOK = true;
        // FktRegFuncPtr pRegisterFunc = &DynamicLibraryHelper::registerFunc;
        // pFunc(pRegisterFunc);
        // osl::Guard aGuard(m_Mutex);
        FktRegFuncPtr pRegisterFunc = &registerFunc;

        CallbackStructure aCallback;
        aCallback.aRegisterFunction = pRegisterFunc;

        aCallback.aCallbackDispatch  = &CallbackDispatch;

        // special parameter for API testing
        if (_aOptions.hasOpt("-forward"))
        {
            aCallback.psForward = _aOptions.getOpt("-forward").getStr();
        }

        // aCallback.pJobList = _pJobList;

        //# aCallback.aStartTest  = &TestResult_startTest;
        //# aCallback.aAddFailure = &TestResult_addFailure;
        //# aCallback.aAddError   = &TestResult_addError;
        //# aCallback.aEndTest    = &TestResult_endTest;
        //# aCallback.aShouldStop = &TestResult_shouldStop;
        //# aCallback.aAddInfo    = &TestResult_addInfo;
        //# aCallback.aEnterNode  = &TestResult_enterNode;
        //# aCallback.aLeaveNode  = &TestResult_leaveNode;

        aCallback.nBits       = FileHelper::createFlags(_aOptions);

        pFunc(&aCallback);

        if (aCallback.nMagic == aCallback.nMagic2)
        {
            // ok internal simple test done.
            m_aFunctionList = m_Functions;
        }
        else
        {
            // ERROR, the function seams not to be what we thing it's to be.
            fprintf(stderr, "error: Internal check failed. Structure inconsistent, Value Magic2 != Magic.\nPlease recompile your test libraries.");
            exit(-1);
        }
    }
    else
    {
        fprintf(stderr, "warning: Function 'registerAllTestFunction' not found.\n");
        fprintf(stderr, "If you think, you are right, build testshl2 completly new.\n");
    }
}

void AutomaticRegisterHelper::CallAll(hTestResult _hResult) const
{
    // can't load the module, break the tests.
    if (m_bLoadLibraryOK == false)
    {
        return;
    }

    for (FunctionList::const_iterator it = m_aFunctionList.begin();
         it != m_aFunctionList.end();
         ++it)
    {
        FktPtr pFunc = *it;
        if (pFunc)
        {
            (pFunc)(_hResult);
        }
    }
}

<|endoftext|>


<commit_before>#include "TestGeneratedProperty.h"
#include "PEG/test.peg.h"
#include "PEG/test2.peg.h"
#include <QtTest/QtTest>

void TestGeneratedProperty::test1()
{
    QtnPropertySetTest1 p;
    QVERIFY(p.name() == "Test1");

    p.a = 2;
    QVERIFY(p.a == 2);
    p.a = 15;
    QVERIFY(p.a.maxValue() == 10);
    QVERIFY(p.a == 2);

    p.a.incrementValue();
    QVERIFY(p.a == 1);
    QVERIFY(p.a > 0 && p.a >= 0 && p.a < 2 && p.a <= 1 && p.a != 3);

    QVERIFY(p.text == "#^{};");
    QVERIFY(p.text.description() == "defrf\"sde\"""deerf3rfderf r g\r\nreg r{}dfrgergfwrewre");

    p.text = tr("new value");
    QVERIFY(p.text == "new value");
}

void TestGeneratedProperty::test2()
{
    QtnPropertySetTest3 n(this);
    n.setName("SS");

    QVERIFY(n.name() == "SS");
    QVERIFY(n.yy.rect == QRect(10, 10, 10, 10));
    QVERIFY(!n.s.a);
    n.s.a = true;
    QVERIFY(n.s.a);
    QVERIFY(n.yy.rect == QRect(1, 1, 1, 1));

    QVERIFY(n.ww == n.bc && n.bc == false);
    n.ww = true;
    QVERIFY(n.ww == n.bc && n.ww == true);
    n.bc = false;
    QVERIFY(n.bc);
    n.m_s = false;
    n.bc = true;
    QVERIFY(n.m_s);
}

void TestGeneratedProperty::testAllPropertyTypes()
{
    QtnPropertySetAllPropertyTypes p;

    QVERIFY(p.ep == COLOR::BLUE);
    p.ep = COLOR::RED;
    switch (p.ep)
    {
    case COLOR::BLUE:
    case COLOR::YELLOW:
        QFAIL("ep expected as COLOR::RED");
        break;

    case COLOR::RED:
        break;

    default:
        QFAIL("ep expected as COLOR::RED");
    }
}

void TestGeneratedProperty::testLoadSave()
{
    {
        QtnPropertySetAllPropertyTypes p;
        QtnPropertySetA p2;

        {
            QtnPropertySet allProperties(nullptr);
            allProperties.addChildProperty(&p, false);
            allProperties.addChildProperty(&p2, false);

            QByteArray data;

            {
                QDataStream s(&data, QIODevice::WriteOnly);
                QVERIFY(allProperties.save(s));
                printf("QDataStream: %d, %d, %d\n", s.byteOrder(), s.floatingPointPrecision(), s.version());
            }

            printf(data.toBase64().data());
            QCOMPARE(data.toBase64(), QByteArray("GYQBAAADngABAAAAAAAAAAABAAAADRmEAQAAA1QAAQAAAAAAAAAAAQAAAA4ZhAEAAAALAAEAAAAIAAAAAAAAAAAPGYQBAAAACwABAAAACAAAAAABAAAAEBmEAQAAAA4AAQAAAAgAAAAAAAAAAAAAABEZhAEAAAAOAAEAAAAIAAAAAAAAAAwAAAASGYQBAAAADgABAAAACAAAAAAAAAAAAAAAExmEAQAAAA4AAQAAAAgAAAAAAAAACQAAABQZhAEAAAASAAEAAAAIAAAAAAAAAAAAAAAAAAAAFRmEAQAAABIAAQAAAAgAAAAAP8mZmaAAAAAAAAAWGYQBAAAAEgABAAAACAAAAAAAAAAAAAAAAAAAABcZhAEAAAASAAEAAAAIAAAAAEBAMzMzMzMzAAAAGBmEAQAAAA4AAQAAAAgAAAAA/////wAAABkZhAEAAAAWAAEAAAAIAAAAAAAAAAgAbgBhAG0AZQAAABoZhAEAAAAaAAEAAAAIAAAAAAAAAAAAAAAA//////////8AAAAbGYQBAAAAGgABAAAACAAAAAAAAAAKAAAACgAAABMAAAATAAAAHBmEAQAAABIAAQAAAAgAAAAAAAAAAAAAAAAAAAAdGYQBAAAAEgABAAAACAAAAAAAAAAJAAAAAgAAAB4ZhAEAAAASAAEAAAAIAAAAAP//////////AAAAHxmEAQAAABIAAQAAAAgAAAAAAAAAIQAAABUAAAAgGYQBAAAADgABAAAACAAAAAAAAAAWAAAAIRmEAQAAAA4AAQAAAAgAAAAAAAAACgAAACIZhAEAAAAOAAEAAAAIAAAAAAAAAAUAAAAjGYQBAAAADgABAAAACAAAAAAAAAAFAAAAJBmEAQAAABUAAQAAAAgAAAAAAf//AAAAAP//AAAAAAAlGYQBAAAAFQABAAAACAAAAAAB/////wAAAAAAAAAAACYZhAEAAAA/AAEAAAAIAAAAAAAAAA4AQwBvAHUAcgBpAGUAcv////9AJAAAAAAAAP////8FAAEAMhAAZAEAAAAAAAAAAAAAAAAAJxmEAQAAADsAAQAAAAgAAAAAAAAACgBBAHIAaQBhAGz/////QDMAAAAAAAD/////BQABADIQAGQBAAAAAAAAAAAAAAAAACgZhAEAAAAKAAEAAAAIAAAAAP////8AAAAyGYQBAAAAJQABAAAAAAAAAAABAAAAMxmEAQAAAAsAAQAAAAgAAAAAAf//////////"));
            //QCOMPARE(data.size(), 933);

            {
                QDataStream s(&data, QIODevice::ReadOnly);
                QVERIFY(allProperties.load(s));
            }

            QString result;
            QVERIFY(allProperties.toStr(result));

            QCOMPARE(result.size(), 925);
        }
    }
}

void TestGeneratedProperty::testJson()
{
    QtnPropertySetAllPropertyTypes p;

    QJsonObject o;
    QVERIFY(p.toJson(o));

    QJsonDocument d(o);
    auto res = d.toJson();
    QCOMPARE(res.size(), 1321);
    res = d.toJson(QJsonDocument::Compact);
    QCOMPARE(res.size(), 752);

    QVERIFY(p.fromJson(o));
}
<commit_msg>updated tests 5<commit_after>#include "TestGeneratedProperty.h"
#include "PEG/test.peg.h"
#include "PEG/test2.peg.h"
#include <QtTest/QtTest>

void TestGeneratedProperty::test1()
{
    QtnPropertySetTest1 p;
    QVERIFY(p.name() == "Test1");

    p.a = 2;
    QVERIFY(p.a == 2);
    p.a = 15;
    QVERIFY(p.a.maxValue() == 10);
    QVERIFY(p.a == 2);

    p.a.incrementValue();
    QVERIFY(p.a == 1);
    QVERIFY(p.a > 0 && p.a >= 0 && p.a < 2 && p.a <= 1 && p.a != 3);

    QVERIFY(p.text == "#^{};");
    QVERIFY(p.text.description() == "defrf\"sde\"""deerf3rfderf r g\r\nreg r{}dfrgergfwrewre");

    p.text = tr("new value");
    QVERIFY(p.text == "new value");
}

void TestGeneratedProperty::test2()
{
    QtnPropertySetTest3 n(this);
    n.setName("SS");

    QVERIFY(n.name() == "SS");
    QVERIFY(n.yy.rect == QRect(10, 10, 10, 10));
    QVERIFY(!n.s.a);
    n.s.a = true;
    QVERIFY(n.s.a);
    QVERIFY(n.yy.rect == QRect(1, 1, 1, 1));

    QVERIFY(n.ww == n.bc && n.bc == false);
    n.ww = true;
    QVERIFY(n.ww == n.bc && n.ww == true);
    n.bc = false;
    QVERIFY(n.bc);
    n.m_s = false;
    n.bc = true;
    QVERIFY(n.m_s);
}

void TestGeneratedProperty::testAllPropertyTypes()
{
    QtnPropertySetAllPropertyTypes p;

    QVERIFY(p.ep == COLOR::BLUE);
    p.ep = COLOR::RED;
    switch (p.ep)
    {
    case COLOR::BLUE:
    case COLOR::YELLOW:
        QFAIL("ep expected as COLOR::RED");
        break;

    case COLOR::RED:
        break;

    default:
        QFAIL("ep expected as COLOR::RED");
    }
}

void TestGeneratedProperty::testLoadSave()
{
    {
        QtnPropertySetAllPropertyTypes p;
        QtnPropertySetA p2;

        {
            QtnPropertySet allProperties(nullptr);
            allProperties.addChildProperty(&p, false);
            allProperties.addChildProperty(&p2, false);

            QByteArray data;

            {
                QDataStream s(&data, QIODevice::WriteOnly);
                s.setVersion(10);
                QVERIFY(allProperties.save(s));
                //printf("QDataStream: %d\n", s.version());
            }

            //printf(data.toBase64().data());
            //QCOMPARE(data.toBase64(), QByteArray("GYQBAAADngABAAAAAAAAAAABAAAADRmEAQAAA1QAAQAAAAAAAAAAAQAAAA4ZhAEAAAALAAEAAAAIAAAAAAAAAAAPGYQBAAAACwABAAAACAAAAAABAAAAEBmEAQAAAA4AAQAAAAgAAAAAAAAAAAAAABEZhAEAAAAOAAEAAAAIAAAAAAAAAAwAAAASGYQBAAAADgABAAAACAAAAAAAAAAAAAAAExmEAQAAAA4AAQAAAAgAAAAAAAAACQAAABQZhAEAAAASAAEAAAAIAAAAAAAAAAAAAAAAAAAAFRmEAQAAABIAAQAAAAgAAAAAP8mZmaAAAAAAAAAWGYQBAAAAEgABAAAACAAAAAAAAAAAAAAAAAAAABcZhAEAAAASAAEAAAAIAAAAAEBAMzMzMzMzAAAAGBmEAQAAAA4AAQAAAAgAAAAA/////wAAABkZhAEAAAAWAAEAAAAIAAAAAAAAAAgAbgBhAG0AZQAAABoZhAEAAAAaAAEAAAAIAAAAAAAAAAAAAAAA//////////8AAAAbGYQBAAAAGgABAAAACAAAAAAAAAAKAAAACgAAABMAAAATAAAAHBmEAQAAABIAAQAAAAgAAAAAAAAAAAAAAAAAAAAdGYQBAAAAEgABAAAACAAAAAAAAAAJAAAAAgAAAB4ZhAEAAAASAAEAAAAIAAAAAP//////////AAAAHxmEAQAAABIAAQAAAAgAAAAAAAAAIQAAABUAAAAgGYQBAAAADgABAAAACAAAAAAAAAAWAAAAIRmEAQAAAA4AAQAAAAgAAAAAAAAACgAAACIZhAEAAAAOAAEAAAAIAAAAAAAAAAUAAAAjGYQBAAAADgABAAAACAAAAAAAAAAFAAAAJBmEAQAAABUAAQAAAAgAAAAAAf//AAAAAP//AAAAAAAlGYQBAAAAFQABAAAACAAAAAAB/////wAAAAAAAAAAACYZhAEAAAA/AAEAAAAIAAAAAAAAAA4AQwBvAHUAcgBpAGUAcv////9AJAAAAAAAAP////8FAAEAMhAAZAEAAAAAAAAAAAAAAAAAJxmEAQAAADsAAQAAAAgAAAAAAAAACgBBAHIAaQBhAGz/////QDMAAAAAAAD/////BQABADIQAGQBAAAAAAAAAAAAAAAAACgZhAEAAAAKAAEAAAAIAAAAAP////8AAAAyGYQBAAAAJQABAAAAAAAAAAABAAAAMxmEAQAAAAsAAQAAAAgAAAAAAf//////////"));
            //QCOMPARE(data.size(), 933);

            {
                QDataStream s(&data, QIODevice::ReadOnly);
                QVERIFY(allProperties.load(s));
            }

            QString result;
            QVERIFY(allProperties.toStr(result));

            QCOMPARE(result.size(), 925);
        }
    }
}

void TestGeneratedProperty::testJson()
{
    QtnPropertySetAllPropertyTypes p;

    QJsonObject o;
    QVERIFY(p.toJson(o));

    QJsonDocument d(o);
    auto res = d.toJson();
    QCOMPARE(res.size(), 1321);
    res = d.toJson(QJsonDocument::Compact);
    QCOMPARE(res.size(), 752);

    QVERIFY(p.fromJson(o));
}
<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "config.h"
#include "include/types.h"
#include "armor.h"
#include "include/Spinlock.h"

#include <errno.h>
#include <fstream>
#include <sys/uio.h>
#include <limits.h>

namespace ceph {

Spinlock buffer_lock("buffer_lock");
atomic_t buffer_total_alloc;

void buffer::list::encode_base64(buffer::list& o)
{
  bufferptr bp(length() * 4 / 3 + 3);
  int l = ceph_armor(bp.c_str(), c_str(), c_str() + length());
  bp.set_length(l);
  o.push_back(bp);
}

void buffer::list::decode_base64(buffer::list& e)
{
  bufferptr bp(e.length() * 3 / 4 + 4);
  int l = ceph_unarmor(bp.c_str(), e.c_str(), e.c_str() + e.length());
  assert(l <= (int)bp.length());
  bp.set_length(l);
  push_back(bp);
}

void buffer::list::rebuild_page_aligned()
{
  std::list<ptr>::iterator p = _buffers.begin();
  while (p != _buffers.end()) {
    // keep anything that's already page sized+aligned
    if (p->is_page_aligned() && p->is_n_page_sized()) {
      /*generic_dout(0) << " segment " << (void*)p->c_str()
		      << " offset " << ((unsigned long)p->c_str() & ~PAGE_MASK)
		      << " length " << p->length()
		      << " " << (p->length() & ~PAGE_MASK) << " ok" << dendl;
      */
      p++;
      continue;
    }

    // consolidate unaligned items, until we get something that is sized+aligned
    list unaligned;
    unsigned offset = 0;
    do {
      /*generic_dout(0) << " segment " << (void*)p->c_str()
		      << " offset " << ((unsigned long)p->c_str() & ~PAGE_MASK)
		      << " length " << p->length() << " " << (p->length() & ~PAGE_MASK)
		      << " overall offset " << offset << " " << (offset & ~PAGE_MASK)
		      << " not ok" << dendl;
      */
      offset += p->length();
      unaligned.push_back(*p);
      _buffers.erase(p++);
    } while (p != _buffers.end() &&
	     (!p->is_page_aligned() ||
	      !p->is_n_page_sized() ||
	      (offset & ~PAGE_MASK)));
    unaligned.rebuild();
    _buffers.insert(p, unaligned._buffers.front());
  }
}

int buffer::list::read_file(const char *fn, bool silent)
{
  struct stat st;
  int fd = ::open(fn, O_RDONLY);
  if (fd < 0) {
    if (!silent) {
      char buf[80];
      cerr << "can't open " << fn << ": " << strerror_r(errno, buf, sizeof(buf)) << std::endl;
    }
    return -errno;
  }
  ::fstat(fd, &st);
  int s = ROUND_UP_TO(st.st_size, PAGE_SIZE);
  bufferptr bp = buffer::create_page_aligned(s);
  int left = st.st_size;
  int got = 0;
  while (left > 0) {
    int r = ::read(fd, (void *)(bp.c_str() + got), left);
    if (r <= 0)
      break;
    got += r;
    left -= r;
  }
  ::close(fd);
  bp.set_length(got);
  append(bp);
  return 0;
}

int buffer::list::write_file(const char *fn, int mode)
{
  int fd = ::open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode);
  if (fd < 0) {
    char buf[80];
    cerr << "can't write " << fn << ": " << strerror_r(errno, buf, sizeof(buf)) << std::endl;
    return -errno;
  }
  int err = write_fd(fd);
  ::close(fd);
  return err;
}

int buffer::list::write_fd(int fd)
{
  // write buffer piecewise
  if (false) {
    for (std::list<ptr>::const_iterator it = _buffers.begin();
	 it != _buffers.end();
	 it++) {
      int left = it->length();
      if (!left)
        continue;
      const char *c = it->c_str();
      while (left > 0) {
	int r = ::write(fd, c, left);
	if (r < 0)
	  return -errno;
	c += r;
	left -= r;
      }
    }
    return 0;
  }

  // use writev!
  iovec iov[IOV_MAX];
  int iovlen = 0;
  ssize_t bytes = 0;

  std::list<ptr>::const_iterator p = _buffers.begin();
  while (true) {
    if (p->length() > 0) {
      iov[iovlen].iov_base = (void *)p->c_str();
      iov[iovlen].iov_len = p->length();
      bytes += p->length();
      iovlen++;
    }
    p++;

    if (iovlen == IOV_MAX-1 ||
	p == _buffers.end()) {
      iovec *start = iov;
      int num = iovlen;
      ssize_t wrote;
    retry:
      wrote = ::writev(fd, start, num);
      if (wrote < 0)
	return -errno;
      if (wrote < bytes) {
	// partial write, recover!
	while ((size_t)wrote >= start[0].iov_len) {
	  wrote -= start[0].iov_len;
	  bytes -= start[0].iov_len;
	  start++;
	  num--;
	}
	if (wrote > 0) {
	  start[0].iov_len -= wrote;
	  start[0].iov_base = (char *)start[0].iov_base + wrote;
	  bytes -= wrote;
	}
	goto retry;
      }
    }
    if (p == _buffers.end())
      break;
  }
  return 0;
}

void buffer::list::hexdump(std::ostream &out) const
{
  out.setf(std::ios::right);
  out.fill('0');

  unsigned per = 16;

  for (unsigned o=0; o<length(); o += per) {
    out << std::hex << std::setw(4) << o << " :";

    unsigned i;
    for (i=0; i<per && o+i<length(); i++) {
      out << " " << std::setw(2) << ((unsigned)(*this)[o+i] & 0xff);
    }
    for (; i<per; i++)
      out << "   ";

    out << " : ";
    for (i=0; i<per && o+i<length(); i++) {
      char c = (*this)[o+i];
      if (isupper(c) || islower(c) || isdigit(c) || c == ' ' || ispunct(c))
	out << c;
      else
	out << '.';
    }
    out << std::dec << std::endl;
  }
  out.unsetf(std::ios::right);
}

}
<commit_msg>buffer: handle write_fd() on empty bufferlist<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "config.h"
#include "include/types.h"
#include "armor.h"
#include "include/Spinlock.h"

#include <errno.h>
#include <fstream>
#include <sys/uio.h>
#include <limits.h>

namespace ceph {

Spinlock buffer_lock("buffer_lock");
atomic_t buffer_total_alloc;

void buffer::list::encode_base64(buffer::list& o)
{
  bufferptr bp(length() * 4 / 3 + 3);
  int l = ceph_armor(bp.c_str(), c_str(), c_str() + length());
  bp.set_length(l);
  o.push_back(bp);
}

void buffer::list::decode_base64(buffer::list& e)
{
  bufferptr bp(e.length() * 3 / 4 + 4);
  int l = ceph_unarmor(bp.c_str(), e.c_str(), e.c_str() + e.length());
  assert(l <= (int)bp.length());
  bp.set_length(l);
  push_back(bp);
}

void buffer::list::rebuild_page_aligned()
{
  std::list<ptr>::iterator p = _buffers.begin();
  while (p != _buffers.end()) {
    // keep anything that's already page sized+aligned
    if (p->is_page_aligned() && p->is_n_page_sized()) {
      /*generic_dout(0) << " segment " << (void*)p->c_str()
		      << " offset " << ((unsigned long)p->c_str() & ~PAGE_MASK)
		      << " length " << p->length()
		      << " " << (p->length() & ~PAGE_MASK) << " ok" << dendl;
      */
      p++;
      continue;
    }

    // consolidate unaligned items, until we get something that is sized+aligned
    list unaligned;
    unsigned offset = 0;
    do {
      /*generic_dout(0) << " segment " << (void*)p->c_str()
		      << " offset " << ((unsigned long)p->c_str() & ~PAGE_MASK)
		      << " length " << p->length() << " " << (p->length() & ~PAGE_MASK)
		      << " overall offset " << offset << " " << (offset & ~PAGE_MASK)
		      << " not ok" << dendl;
      */
      offset += p->length();
      unaligned.push_back(*p);
      _buffers.erase(p++);
    } while (p != _buffers.end() &&
	     (!p->is_page_aligned() ||
	      !p->is_n_page_sized() ||
	      (offset & ~PAGE_MASK)));
    unaligned.rebuild();
    _buffers.insert(p, unaligned._buffers.front());
  }
}

int buffer::list::read_file(const char *fn, bool silent)
{
  struct stat st;
  int fd = ::open(fn, O_RDONLY);
  if (fd < 0) {
    if (!silent) {
      char buf[80];
      cerr << "can't open " << fn << ": " << strerror_r(errno, buf, sizeof(buf)) << std::endl;
    }
    return -errno;
  }
  ::fstat(fd, &st);
  int s = ROUND_UP_TO(st.st_size, PAGE_SIZE);
  bufferptr bp = buffer::create_page_aligned(s);
  int left = st.st_size;
  int got = 0;
  while (left > 0) {
    int r = ::read(fd, (void *)(bp.c_str() + got), left);
    if (r <= 0)
      break;
    got += r;
    left -= r;
  }
  ::close(fd);
  bp.set_length(got);
  append(bp);
  return 0;
}

int buffer::list::write_file(const char *fn, int mode)
{
  int fd = ::open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode);
  if (fd < 0) {
    char buf[80];
    cerr << "can't write " << fn << ": " << strerror_r(errno, buf, sizeof(buf)) << std::endl;
    return -errno;
  }
  int err = write_fd(fd);
  ::close(fd);
  return err;
}

int buffer::list::write_fd(int fd)
{
  // write buffer piecewise
  if (false) {
    for (std::list<ptr>::const_iterator it = _buffers.begin();
	 it != _buffers.end();
	 it++) {
      int left = it->length();
      if (!left)
        continue;
      const char *c = it->c_str();
      while (left > 0) {
	int r = ::write(fd, c, left);
	if (r < 0)
	  return -errno;
	c += r;
	left -= r;
      }
    }
    return 0;
  }

  // use writev!
  iovec iov[IOV_MAX];
  int iovlen = 0;
  ssize_t bytes = 0;

  std::list<ptr>::const_iterator p = _buffers.begin();
  while (p != _buffers.end()) {
    if (p->length() > 0) {
      iov[iovlen].iov_base = (void *)p->c_str();
      iov[iovlen].iov_len = p->length();
      bytes += p->length();
      iovlen++;
    }
    p++;

    if (iovlen == IOV_MAX-1 ||
	p == _buffers.end()) {
      iovec *start = iov;
      int num = iovlen;
      ssize_t wrote;
    retry:
      wrote = ::writev(fd, start, num);
      if (wrote < 0)
	return -errno;
      if (wrote < bytes) {
	// partial write, recover!
	while ((size_t)wrote >= start[0].iov_len) {
	  wrote -= start[0].iov_len;
	  bytes -= start[0].iov_len;
	  start++;
	  num--;
	}
	if (wrote > 0) {
	  start[0].iov_len -= wrote;
	  start[0].iov_base = (char *)start[0].iov_base + wrote;
	  bytes -= wrote;
	}
	goto retry;
      }
    }
  }
  return 0;
}

void buffer::list::hexdump(std::ostream &out) const
{
  out.setf(std::ios::right);
  out.fill('0');

  unsigned per = 16;

  for (unsigned o=0; o<length(); o += per) {
    out << std::hex << std::setw(4) << o << " :";

    unsigned i;
    for (i=0; i<per && o+i<length(); i++) {
      out << " " << std::setw(2) << ((unsigned)(*this)[o+i] & 0xff);
    }
    for (; i<per; i++)
      out << "   ";

    out << " : ";
    for (i=0; i<per && o+i<length(); i++) {
      char c = (*this)[o+i];
      if (isupper(c) || islower(c) || isdigit(c) || c == ' ' || ispunct(c))
	out << c;
      else
	out << '.';
    }
    out << std::dec << std::endl;
  }
  out.unsetf(std::ios::right);
}

}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/mc/mc.H $       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mc.H
/// @brief Subroutines to manipulate the memory controller
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#ifndef _MSS_MC_H_
#define _MSS_MC_H_

#include <fapi2.H>

#include <p9_mc_scom_addresses.H>
#include <p9_mc_scom_addresses_fld.H>
#include <lib/mss_attribute_accessors.H>
#include <lib/utils/scom.H>

namespace mss
{

// I have a dream that the xlate code can be shared among controllers. So, I drive the
// engine from a set of traits. This might be folly. Allow me to dream. BRS

///
/// @class mcTraits
/// @brief a collection of traits associated with the MC
/// @tparam T, fapi2::TargetType representing the MC
///
template< fapi2::TargetType T >
class mcTraits;

///
/// @class mcTraits
/// @brief a collection of traits associated with the Centaur controller
///
template<>
class mcTraits<fapi2::TARGET_TYPE_MBA>
{
};

///
/// @class mcTraits
/// @brief a collection of traits associated with the Nimbus controller
///
template<>
class mcTraits<fapi2::TARGET_TYPE_MCA>
{
    public:
        enum
        {
            SLOT0_VALID = MCS_PORT02_MCP0XLT0_SLOT0_VALID,
            SLOT0_ROW15_VALID = MCS_PORT02_MCP0XLT0_SLOT0_ROW15_VALID,
            SLOT0_M1_VALID = MCS_PORT02_MCP0XLT0_SLOT0_M1_VALID,
            M1_BIT_MAP = MCS_PORT02_MCP0XLT0_M1_BIT_MAP,
            M1_BIT_MAP_LEN = MCS_PORT02_MCP0XLT0_M1_BIT_MAP_LEN,
            D_BIT_MAP = MCS_PORT02_MCP0XLT0_D_BIT_MAP,
            D_BIT_MAP_LEN = MCS_PORT02_MCP0XLT0_D_BIT_MAP_LEN,
            R15_BIT_MAP = MCS_PORT02_MCP0XLT0_R15_BIT_MAP,
            R15_BIT_MAP_LEN = MCS_PORT02_MCP0XLT0_R15_BIT_MAP_LEN,
            COL4_BIT_MAP = MCS_PORT02_MCP0XLT1_COL4_BIT_MAP,
            COL4_BIT_MAP_LEN = MCS_PORT02_MCP0XLT1_COL4_BIT_MAP_LEN,
            COL5_BIT_MAP = MCS_PORT02_MCP0XLT1_COL5_BIT_MAP,
            COL5_BIT_MAP_LEN = MCS_PORT02_MCP0XLT1_COL5_BIT_MAP_LEN,
            COL6_BIT_MAP = MCS_PORT02_MCP0XLT1_COL6_BIT_MAP,
            COL6_BIT_MAP_LEN = MCS_PORT02_MCP0XLT1_COL6_BIT_MAP_LEN,
            COL7_BIT_MAP = MCS_PORT02_MCP0XLT1_COL7_BIT_MAP,
            COL7_BIT_MAP_LEN = MCS_PORT02_MCP0XLT1_COL7_BIT_MAP_LEN,
            COL8_BIT_MAP = MCS_PORT02_MCP0XLT2_COL8_BIT_MAP,
            COL8_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_COL8_BIT_MAP_LEN,
            COL9_BIT_MAP = MCS_PORT02_MCP0XLT2_COL9_BIT_MAP,
            COL9_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_COL9_BIT_MAP_LEN,
            BANK0_BIT_MAP = MCS_PORT02_MCP0XLT2_BANK0_BIT_MAP,
            BANK0_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_BANK0_BIT_MAP_LEN,
            BANK1_BIT_MAP = MCS_PORT02_MCP0XLT2_BANK1_BIT_MAP,
            BANK1_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_BANK1_BIT_MAP_LEN,
            BANK_GROUP0_BIT_MAP = MCS_PORT02_MCP0XLT2_BANK_GROUP0_BIT_MAP,
            BANK_GROUP0_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_BANK_GROUP0_BIT_MAP_LEN,
            BANK_GROUP1_BIT_MAP = MCS_PORT02_MCP0XLT2_BANK_GROUP1_BIT_MAP,
            BANK_GROUP1_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_BANK_GROUP1_BIT_MAP_LEN,
        };

};

// Why two enums? Two reasons. First, we need these to be contiguous, and
// we can't always guarentee the symbols will be generated as such. Second,
// we want to be 0'based so that we can use a static array as a table and
// it's probably not possible for the symbols to be 0'based.
enum
{
    THIS_ENTRY_VALID = 0,
    VALUE_OF_D_BIT_INDEX = 1,
    GB12_ENABLE_INDEX = 2,
    MASTER_BIT_0_VALID_INDEX = 3,
    MASTER_BIT_1_VALID_INDEX = 4,
    SLAVE_BIT_0_VALID_INDEX = 5,
    SLAVE_BIT_1_VALID_INDEX = 6,
    SLAVE_BIT_2_VALID_INDEX = 7,
    ROW_BIT_15_VALID_INDEX = 8,
    ROW_BIT_16_VALID_INDEX = 9,
    ROW_BIT_17_VALID_INDEX = 10,
    BANK_BIT_2_VALID_INDEX = 11,
    DIMM_BIT_INDEX = 12,
    MASTER_BIT_0_INDEX = 13,
    MASTER_BIT_1_INDEX = 14,
    SLAVE_BIT_0_INDEX = 15,
    SLAVE_BIT_1_INDEX = 16,
    SLAVE_BIT_2_INDEX = 17,
    ROW_17_INDEX = 18,
    ROW_16_INDEX = 19,
    COLUMN_4_INDEX = 20,
    COLUMN_5_INDEX = 21,
    COLUMN_6_INDEX = 22,
    COLUMN_7_INDEX = 23,
    COLUMN_8_INDEX = 24,
    COLUMN_9_INDEX = 25,
    BANK_0_INDEX = 26,
    BANK_1_INDEX = 27,
    BANK_2_INDEX = 28,
    BANK_GROUP_0_BIT_INDEX = 29,
    BANK_GROUP_1_BIT_INDEX = 30,
    MAX_TRANSLATIONS = 31,
};

///
/// @class mss::mc
/// @brief The memory controller class
/// @tparam T the fapi2::TargetType of the controller
///
template< fapi2::TargetType T >
class mc
{

    public:
        ///
        /// @brief Perform initializations for the MC
        /// @tparam T the fapi2::TargetType
        /// @param[in] i_target the target which has the MCs to initialize
        /// @return FAPI2_RC_SUCCESS iff ok
        ///
        fapi2::ReturnCode scominit(const fapi2::Target<T>& i_target);

        ///
        /// @brief Perform initializations of the MC translation
        /// @tparam P the fapi2::TargetType of the port
        /// @tparam TT the typename of the traits
        /// @param[in] i_target the target which has the MCA to map
        /// @return FAPI2_RC_SUCCESS iff ok
        ///
        template< fapi2::TargetType P, typename TT = mcTraits<T> >
        fapi2::ReturnCode setup_xlate_map(const fapi2::Target<P>& i_target);

};

}

#endif
<commit_msg>Implementing thermal_init<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/mc/mc.H $       */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mc.H
/// @brief Subroutines to manipulate the memory controller
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#ifndef _MSS_MC_H_
#define _MSS_MC_H_

#include <fapi2.H>

#include <p9_mc_scom_addresses.H>
#include <p9_mc_scom_addresses_fld.H>
#include <lib/mss_attribute_accessors.H>
#include <lib/utils/scom.H>

namespace mss
{

// I have a dream that the xlate code can be shared among controllers. So, I drive the
// engine from a set of traits. This might be folly. Allow me to dream. BRS

///
/// @class mcTraits
/// @brief a collection of traits associated with the MC
/// @tparam T, fapi2::TargetType representing the MC
///
template< fapi2::TargetType T >
class mcTraits;

///
/// @class mcTraits
/// @brief a collection of traits associated with the Centaur controller
///
template<>
class mcTraits<fapi2::TARGET_TYPE_MBA>
{
};

///
/// @class mcTraits
/// @brief a collection of traits associated with the Nimbus controller
///
template<>
class mcTraits<fapi2::TARGET_TYPE_MCA>
{
    public:
        enum
        {
            SLOT0_VALID = MCS_PORT02_MCP0XLT0_SLOT0_VALID,
            SLOT0_ROW15_VALID = MCS_PORT02_MCP0XLT0_SLOT0_ROW15_VALID,
            SLOT0_M1_VALID = MCS_PORT02_MCP0XLT0_SLOT0_M1_VALID,
            M1_BIT_MAP = MCS_PORT02_MCP0XLT0_M1_BIT_MAP,
            M1_BIT_MAP_LEN = MCS_PORT02_MCP0XLT0_M1_BIT_MAP_LEN,
            D_BIT_MAP = MCS_PORT02_MCP0XLT0_D_BIT_MAP,
            D_BIT_MAP_LEN = MCS_PORT02_MCP0XLT0_D_BIT_MAP_LEN,
            R15_BIT_MAP = MCS_PORT02_MCP0XLT0_R15_BIT_MAP,
            R15_BIT_MAP_LEN = MCS_PORT02_MCP0XLT0_R15_BIT_MAP_LEN,
            COL4_BIT_MAP = MCS_PORT02_MCP0XLT1_COL4_BIT_MAP,
            COL4_BIT_MAP_LEN = MCS_PORT02_MCP0XLT1_COL4_BIT_MAP_LEN,
            COL5_BIT_MAP = MCS_PORT02_MCP0XLT1_COL5_BIT_MAP,
            COL5_BIT_MAP_LEN = MCS_PORT02_MCP0XLT1_COL5_BIT_MAP_LEN,
            COL6_BIT_MAP = MCS_PORT02_MCP0XLT1_COL6_BIT_MAP,
            COL6_BIT_MAP_LEN = MCS_PORT02_MCP0XLT1_COL6_BIT_MAP_LEN,
            COL7_BIT_MAP = MCS_PORT02_MCP0XLT1_COL7_BIT_MAP,
            COL7_BIT_MAP_LEN = MCS_PORT02_MCP0XLT1_COL7_BIT_MAP_LEN,
            COL8_BIT_MAP = MCS_PORT02_MCP0XLT2_COL8_BIT_MAP,
            COL8_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_COL8_BIT_MAP_LEN,
            COL9_BIT_MAP = MCS_PORT02_MCP0XLT2_COL9_BIT_MAP,
            COL9_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_COL9_BIT_MAP_LEN,
            BANK0_BIT_MAP = MCS_PORT02_MCP0XLT2_BANK0_BIT_MAP,
            BANK0_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_BANK0_BIT_MAP_LEN,
            BANK1_BIT_MAP = MCS_PORT02_MCP0XLT2_BANK1_BIT_MAP,
            BANK1_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_BANK1_BIT_MAP_LEN,
            BANK_GROUP0_BIT_MAP = MCS_PORT02_MCP0XLT2_BANK_GROUP0_BIT_MAP,
            BANK_GROUP0_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_BANK_GROUP0_BIT_MAP_LEN,
            BANK_GROUP1_BIT_MAP = MCS_PORT02_MCP0XLT2_BANK_GROUP1_BIT_MAP,
            BANK_GROUP1_BIT_MAP_LEN = MCS_PORT02_MCP0XLT2_BANK_GROUP1_BIT_MAP_LEN,
        };

};

// Why two enums? Two reasons. First, we need these to be contiguous, and
// we can't always guarentee the symbols will be generated as such. Second,
// we want to be 0'based so that we can use a static array as a table and
// it's probably not possible for the symbols to be 0'based.
enum
{
    THIS_ENTRY_VALID = 0,
    VALUE_OF_D_BIT_INDEX = 1,
    GB12_ENABLE_INDEX = 2,
    MASTER_BIT_0_VALID_INDEX = 3,
    MASTER_BIT_1_VALID_INDEX = 4,
    SLAVE_BIT_0_VALID_INDEX = 5,
    SLAVE_BIT_1_VALID_INDEX = 6,
    SLAVE_BIT_2_VALID_INDEX = 7,
    ROW_BIT_15_VALID_INDEX = 8,
    ROW_BIT_16_VALID_INDEX = 9,
    ROW_BIT_17_VALID_INDEX = 10,
    BANK_BIT_2_VALID_INDEX = 11,
    DIMM_BIT_INDEX = 12,
    MASTER_BIT_0_INDEX = 13,
    MASTER_BIT_1_INDEX = 14,
    SLAVE_BIT_0_INDEX = 15,
    SLAVE_BIT_1_INDEX = 16,
    SLAVE_BIT_2_INDEX = 17,
    ROW_17_INDEX = 18,
    ROW_16_INDEX = 19,
    COLUMN_4_INDEX = 20,
    COLUMN_5_INDEX = 21,
    COLUMN_6_INDEX = 22,
    COLUMN_7_INDEX = 23,
    COLUMN_8_INDEX = 24,
    COLUMN_9_INDEX = 25,
    BANK_0_INDEX = 26,
    BANK_1_INDEX = 27,
    BANK_2_INDEX = 28,
    BANK_GROUP_0_BIT_INDEX = 29,
    BANK_GROUP_1_BIT_INDEX = 30,
    MAX_TRANSLATIONS = 31,
};

namespace mc
{

///
/// @brief safemode throttle values defined from MRW attributes
/// @param[in] i_target the MCA target
/// @return FAPI2_RC_SUCCESS iff ok
/// @note sets safemode values for emergency mode and regular throttling
///
fapi2::ReturnCode thermal_throttle_scominit (const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target);
///
/// @brief Disable emergency mode throttle for thermal_init
/// @param[in] i_target the MCS target
/// @return FAPI_RC_SUCCESS iff ok
/// @note Clears MCMODE0_ENABLE_EMER_THROTTLE bit in MCSMODE0
///
fapi2::ReturnCode disable_emergency_throttle (const fapi2::Target<fapi2::TARGET_TYPE_MCS>& i_target);

///
/// @brief Perform initializations of the MC translation
/// @tparam P the fapi2::TargetType of the port
/// @tparam TT the mcTraits associated with P
/// @param[in] i_target the target which has the MCA to map
/// @return FAPI2_RC_SUCCESS iff ok
///
template< fapi2::TargetType P,  typename TT = mcTraits<P> >
fapi2::ReturnCode setup_xlate_map(const fapi2::Target<P>& i_target);
} //mc
} //mss
#endif
<|endoftext|>


<commit_before>//===----- RISCVMergeBaseOffset.cpp - Optimise address calculations  ------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Merge the offset of address calculation into the offset field
// of instructions in a global address lowering sequence. This pass transforms:
//   lui  vreg1, %hi(s)
//   addi vreg2, vreg1, %lo(s)
//   addi vreg3, verg2, Offset
//
//   Into:
//   lui  vreg1, %hi(s+Offset)
//   addi vreg2, vreg1, %lo(s+Offset)
//
// The transformation is carried out under certain conditions:
// 1) The offset field in the base of global address lowering sequence is zero.
// 2) The lowered global address has only one use.
//
// The offset field can be in a different form. This pass handles all of them.
//===----------------------------------------------------------------------===//

#include "RISCV.h"
#include "RISCVTargetMachine.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetOptions.h"
#include <set>
using namespace llvm;

#define DEBUG_TYPE "riscv-merge-base-offset"
#define RISCV_MERGE_BASE_OFFSET_NAME "RISCV Merge Base Offset"
namespace {

struct RISCVMergeBaseOffsetOpt : public MachineFunctionPass {
  static char ID;
  const MachineFunction *MF;
  bool runOnMachineFunction(MachineFunction &Fn) override;
  bool detectLuiAddiGlobal(MachineInstr &LUI, MachineInstr *&ADDI);

  bool detectAndFoldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI);
  void foldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI, MachineInstr &Tail,
                  int64_t Offset);
  bool matchLargeOffset(MachineInstr &TailAdd, unsigned GSReg, int64_t &Offset);
  RISCVMergeBaseOffsetOpt() : MachineFunctionPass(ID) {}

  MachineFunctionProperties getRequiredProperties() const override {
    return MachineFunctionProperties().set(
        MachineFunctionProperties::Property::IsSSA);
  }

  StringRef getPassName() const override {
    return RISCV_MERGE_BASE_OFFSET_NAME;
  }

private:
  MachineRegisterInfo *MRI;
  std::set<MachineInstr *> DeadInstrs;
};
}; // end anonymous namespace

char RISCVMergeBaseOffsetOpt::ID = 0;
INITIALIZE_PASS(RISCVMergeBaseOffsetOpt, "riscv-merge-base-offset",
                RISCV_MERGE_BASE_OFFSET_NAME, false, false)

// Detect the pattern:
//   lui   vreg1, %hi(s)
//   addi  vreg2, vreg1, %lo(s)
//
//   Pattern only accepted if:
//     1) ADDI has only one use.
//     2) LUI has only one use; which is the ADDI.
//     3) Both ADDI and LUI have GlobalAddress type which indicates that these
//        are generated from global address lowering.
//     4) Offset value in the Global Address is 0.
bool RISCVMergeBaseOffsetOpt::detectLuiAddiGlobal(MachineInstr &HiLUI,
                                                  MachineInstr *&LoADDI) {
  if (HiLUI.getOpcode() != RISCV::LUI ||
      HiLUI.getOperand(1).getTargetFlags() != RISCVII::MO_HI ||
      HiLUI.getOperand(1).getType() != MachineOperand::MO_GlobalAddress ||
      HiLUI.getOperand(1).getOffset() != 0 ||
      !MRI->hasOneUse(HiLUI.getOperand(0).getReg()))
    return false;
  unsigned HiLuiDestReg = HiLUI.getOperand(0).getReg();
  LoADDI = MRI->use_begin(HiLuiDestReg)->getParent();
  if (LoADDI->getOpcode() != RISCV::ADDI ||
      LoADDI->getOperand(2).getTargetFlags() != RISCVII::MO_LO ||
      LoADDI->getOperand(2).getType() != MachineOperand::MO_GlobalAddress ||
      LoADDI->getOperand(2).getOffset() != 0 ||
      !MRI->hasOneUse(LoADDI->getOperand(0).getReg()))
    return false;
  return true;
}

// Update the offset in HiLUI and LoADDI instructions.
// Delete the tail instruction and update all the uses to use the
// output from LoADDI.
void RISCVMergeBaseOffsetOpt::foldOffset(MachineInstr &HiLUI,
                                         MachineInstr &LoADDI,
                                         MachineInstr &Tail, int64_t Offset) {
  // Put the offset back in HiLUI and the LoADDI
  HiLUI.getOperand(1).setOffset(Offset);
  LoADDI.getOperand(2).setOffset(Offset);
  // Delete the tail instruction.
  DeadInstrs.insert(&Tail);
  MRI->replaceRegWith(Tail.getOperand(0).getReg(),
                      LoADDI.getOperand(0).getReg());
  LLVM_DEBUG(dbgs() << "  Merged offset " << Offset << " into base.\n"
                    << "     " << HiLUI << "     " << LoADDI;);
}

// Detect patterns for large offsets that are passed into an ADD instruction.
//
//                     Base address lowering is of the form:
//                        HiLUI:  lui   vreg1, %hi(s)
//                       LoADDI:  addi  vreg2, vreg1, %lo(s)
//                       /                                  \
//                      /                                    \
//                     /                                      \
//                    /  The large offset can be of two forms: \
//  1) Offset that has non zero bits in lower      2) Offset that has non zero
//     12 bits and upper 20 bits                      bits in upper 20 bits only
//   OffseLUI: lui   vreg3, 4
// OffsetTail: addi  voff, vreg3, 188                OffsetTail: lui  voff, 128
//                    \                                        /
//                     \                                      /
//                      \                                    /
//                       \                                  /
//                         TailAdd: add  vreg4, vreg2, voff
bool RISCVMergeBaseOffsetOpt::matchLargeOffset(MachineInstr &TailAdd,
                                               unsigned GAReg,
                                               int64_t &Offset) {
  assert((TailAdd.getOpcode() == RISCV::ADD) && "Expected ADD instruction!");
  unsigned Rs = TailAdd.getOperand(1).getReg();
  unsigned Rt = TailAdd.getOperand(2).getReg();
  unsigned Reg = Rs == GAReg ? Rt : Rs;

  // Can't fold if the register has more than one use.
  if (!MRI->hasOneUse(Reg))
    return false;
  // This can point to an ADDI or a LUI:
  MachineInstr &OffsetTail = *MRI->getVRegDef(Reg);
  if (OffsetTail.getOpcode() == RISCV::ADDI) {
    // The offset value has non zero bits in both %hi and %lo parts.
    // Detect an ADDI that feeds from a LUI instruction.
    MachineOperand &AddiImmOp = OffsetTail.getOperand(2);
    if (AddiImmOp.getTargetFlags() != RISCVII::MO_None)
      return false;
    int64_t OffLo = AddiImmOp.getImm();
    MachineInstr &OffsetLui =
        *MRI->getVRegDef(OffsetTail.getOperand(1).getReg());
    MachineOperand &LuiImmOp = OffsetLui.getOperand(1);
    if (OffsetLui.getOpcode() != RISCV::LUI ||
        LuiImmOp.getTargetFlags() != RISCVII::MO_None ||
        !MRI->hasOneUse(OffsetLui.getOperand(0).getReg()))
      return false;
    int64_t OffHi = OffsetLui.getOperand(1).getImm();
    Offset = (OffHi << 12) + OffLo;
    LLVM_DEBUG(dbgs() << "  Offset Instrs: " << OffsetTail
                      << "                 " << OffsetLui);
    DeadInstrs.insert(&OffsetTail);
    DeadInstrs.insert(&OffsetLui);
    return true;
  } else if (OffsetTail.getOpcode() == RISCV::LUI) {
    // The offset value has all zero bits in the lower 12 bits. Only LUI
    // exists.
    LLVM_DEBUG(dbgs() << "  Offset Instr: " << OffsetTail);
    Offset = OffsetTail.getOperand(1).getImm() << 12;
    DeadInstrs.insert(&OffsetTail);
    return true;
  }
  return false;
}

bool RISCVMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &HiLUI,
                                                  MachineInstr &LoADDI) {
  unsigned DestReg = LoADDI.getOperand(0).getReg();
  assert(MRI->hasOneUse(DestReg) && "expected one use for LoADDI");
  // LoADDI has only one use.
  MachineInstr &Tail = *MRI->use_begin(DestReg)->getParent();
  switch (Tail.getOpcode()) {
  default:
    LLVM_DEBUG(dbgs() << "Don't know how to get offset from this instr:"
                      << Tail);
    return false;
  case RISCV::ADDI: {
    // Offset is simply an immediate operand.
    int64_t Offset = Tail.getOperand(2).getImm();
    LLVM_DEBUG(dbgs() << "  Offset Instr: " << Tail);
    foldOffset(HiLUI, LoADDI, Tail, Offset);
    return true;
  } break;
  case RISCV::ADD: {
    // The offset is too large to fit in the immediate field of ADDI.
    // This can be in two forms:
    // 1) LUI hi_Offset followed by:
    //    ADDI lo_offset
    //    This happens in case the offset has non zero bits in
    //    both hi 20 and lo 12 bits.
    // 2) LUI (offset20)
    //    This happens in case the lower 12 bits of the offset are zeros.
    int64_t Offset;
    if (!matchLargeOffset(Tail, DestReg, Offset))
      return false;
    foldOffset(HiLUI, LoADDI, Tail, Offset);
    return true;
  } break;
  case RISCV::LB:
  case RISCV::LH:
  case RISCV::LW:
  case RISCV::LBU:
  case RISCV::LHU:
  case RISCV::LWU:
  case RISCV::LD:
  case RISCV::FLW:
  case RISCV::FLD:
  case RISCV::SB:
  case RISCV::SH:
  case RISCV::SW:
  case RISCV::SD:
  case RISCV::FSW:
  case RISCV::FSD: {
    // Transforms the sequence:            Into:
    // HiLUI:  lui vreg1, %hi(foo)          --->  lui vreg1, %hi(foo+8)
    // LoADDI: addi vreg2, vreg1, %lo(foo)  --->  lw vreg3, lo(foo+8)(vreg1)
    // Tail:   lw vreg3, 8(vreg2)
    if (Tail.getOperand(1).isFI())
      return false;
    // Register defined by LoADDI should be used in the base part of the
    // load\store instruction. Otherwise, no folding possible.
    unsigned BaseAddrReg = Tail.getOperand(1).getReg();
    if (DestReg != BaseAddrReg)
      return false;
    MachineOperand &TailImmOp = Tail.getOperand(2);
    int64_t Offset = TailImmOp.getImm();
    // Update the offsets in global address lowering.
    HiLUI.getOperand(1).setOffset(Offset);
    // Update the immediate in the Tail instruction to add the offset.
    Tail.RemoveOperand(2);
    MachineOperand &ImmOp = LoADDI.getOperand(2);
    ImmOp.setOffset(Offset);
    Tail.addOperand(ImmOp);
    // Update the base reg in the Tail instruction to feed from LUI.
    // Output of HiLUI is only used in LoADDI, no need to use
    // MRI->replaceRegWith().
    Tail.getOperand(1).setReg(HiLUI.getOperand(0).getReg());
    DeadInstrs.insert(&LoADDI);
    return true;
  } break;
  }
  return false;
}

bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) {
  if (skipFunction(Fn.getFunction()))
    return false;

  DeadInstrs.clear();
  MRI = &Fn.getRegInfo();
  for (MachineBasicBlock &MBB : Fn) {
    LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n");
    for (MachineInstr &HiLUI : MBB) {
      MachineInstr *LoADDI = nullptr;
      if (!detectLuiAddiGlobal(HiLUI, LoADDI))
        continue;
      LLVM_DEBUG(dbgs() << "  Found lowered global address with one use: "
                        << *LoADDI->getOperand(2).getGlobal() << "\n");
      // If the use count is only one, merge the offset
      detectAndFoldOffset(HiLUI, *LoADDI);
    }
  }
  // Delete dead instructions.
  for (auto *MI : DeadInstrs)
    MI->eraseFromParent();
  return true;
}

/// Returns an instance of the Merge Base Offset Optimization pass.
FunctionPass *llvm::createRISCVMergeBaseOffsetOptPass() {
  return new RISCVMergeBaseOffsetOpt();
}
<commit_msg>Test commit.<commit_after>//===----- RISCVMergeBaseOffset.cpp - Optimise address calculations  ------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Merge the offset of address calculation into the offset field
// of instructions in a global address lowering sequence. This pass transforms:
//   lui  vreg1, %hi(s)
//   addi vreg2, vreg1, %lo(s)
//   addi vreg3, verg2, Offset
//
//   Into:
//   lui  vreg1, %hi(s+Offset)
//   addi vreg2, vreg1, %lo(s+Offset)
//
// The transformation is carried out under certain conditions:
// 1) The offset field in the base of global address lowering sequence is zero.
// 2) The lowered global address has only one use.
//
// The offset field can be in a different form. This pass handles all of them.
//===----------------------------------------------------------------------===//

#include "RISCV.h"
#include "RISCVTargetMachine.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetOptions.h"
#include <set>
using namespace llvm;

#define DEBUG_TYPE "riscv-merge-base-offset"
#define RISCV_MERGE_BASE_OFFSET_NAME "RISCV Merge Base Offset"
namespace {

struct RISCVMergeBaseOffsetOpt : public MachineFunctionPass {
  static char ID;
  const MachineFunction *MF;
  bool runOnMachineFunction(MachineFunction &Fn) override;
  bool detectLuiAddiGlobal(MachineInstr &LUI, MachineInstr *&ADDI);

  bool detectAndFoldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI);
  void foldOffset(MachineInstr &HiLUI, MachineInstr &LoADDI, MachineInstr &Tail,
                  int64_t Offset);
  bool matchLargeOffset(MachineInstr &TailAdd, unsigned GSReg, int64_t &Offset);
  RISCVMergeBaseOffsetOpt() : MachineFunctionPass(ID) {}

  MachineFunctionProperties getRequiredProperties() const override {
    return MachineFunctionProperties().set(
        MachineFunctionProperties::Property::IsSSA);
  }

  StringRef getPassName() const override {
    return RISCV_MERGE_BASE_OFFSET_NAME;
  }

private:
  MachineRegisterInfo *MRI;
  std::set<MachineInstr *> DeadInstrs;
};
} // end anonymous namespace

char RISCVMergeBaseOffsetOpt::ID = 0;
INITIALIZE_PASS(RISCVMergeBaseOffsetOpt, "riscv-merge-base-offset",
                RISCV_MERGE_BASE_OFFSET_NAME, false, false)

// Detect the pattern:
//   lui   vreg1, %hi(s)
//   addi  vreg2, vreg1, %lo(s)
//
//   Pattern only accepted if:
//     1) ADDI has only one use.
//     2) LUI has only one use; which is the ADDI.
//     3) Both ADDI and LUI have GlobalAddress type which indicates that these
//        are generated from global address lowering.
//     4) Offset value in the Global Address is 0.
bool RISCVMergeBaseOffsetOpt::detectLuiAddiGlobal(MachineInstr &HiLUI,
                                                  MachineInstr *&LoADDI) {
  if (HiLUI.getOpcode() != RISCV::LUI ||
      HiLUI.getOperand(1).getTargetFlags() != RISCVII::MO_HI ||
      HiLUI.getOperand(1).getType() != MachineOperand::MO_GlobalAddress ||
      HiLUI.getOperand(1).getOffset() != 0 ||
      !MRI->hasOneUse(HiLUI.getOperand(0).getReg()))
    return false;
  unsigned HiLuiDestReg = HiLUI.getOperand(0).getReg();
  LoADDI = MRI->use_begin(HiLuiDestReg)->getParent();
  if (LoADDI->getOpcode() != RISCV::ADDI ||
      LoADDI->getOperand(2).getTargetFlags() != RISCVII::MO_LO ||
      LoADDI->getOperand(2).getType() != MachineOperand::MO_GlobalAddress ||
      LoADDI->getOperand(2).getOffset() != 0 ||
      !MRI->hasOneUse(LoADDI->getOperand(0).getReg()))
    return false;
  return true;
}

// Update the offset in HiLUI and LoADDI instructions.
// Delete the tail instruction and update all the uses to use the
// output from LoADDI.
void RISCVMergeBaseOffsetOpt::foldOffset(MachineInstr &HiLUI,
                                         MachineInstr &LoADDI,
                                         MachineInstr &Tail, int64_t Offset) {
  // Put the offset back in HiLUI and the LoADDI
  HiLUI.getOperand(1).setOffset(Offset);
  LoADDI.getOperand(2).setOffset(Offset);
  // Delete the tail instruction.
  DeadInstrs.insert(&Tail);
  MRI->replaceRegWith(Tail.getOperand(0).getReg(),
                      LoADDI.getOperand(0).getReg());
  LLVM_DEBUG(dbgs() << "  Merged offset " << Offset << " into base.\n"
                    << "     " << HiLUI << "     " << LoADDI;);
}

// Detect patterns for large offsets that are passed into an ADD instruction.
//
//                     Base address lowering is of the form:
//                        HiLUI:  lui   vreg1, %hi(s)
//                       LoADDI:  addi  vreg2, vreg1, %lo(s)
//                       /                                  \
//                      /                                    \
//                     /                                      \
//                    /  The large offset can be of two forms: \
//  1) Offset that has non zero bits in lower      2) Offset that has non zero
//     12 bits and upper 20 bits                      bits in upper 20 bits only
//   OffseLUI: lui   vreg3, 4
// OffsetTail: addi  voff, vreg3, 188                OffsetTail: lui  voff, 128
//                    \                                        /
//                     \                                      /
//                      \                                    /
//                       \                                  /
//                         TailAdd: add  vreg4, vreg2, voff
bool RISCVMergeBaseOffsetOpt::matchLargeOffset(MachineInstr &TailAdd,
                                               unsigned GAReg,
                                               int64_t &Offset) {
  assert((TailAdd.getOpcode() == RISCV::ADD) && "Expected ADD instruction!");
  unsigned Rs = TailAdd.getOperand(1).getReg();
  unsigned Rt = TailAdd.getOperand(2).getReg();
  unsigned Reg = Rs == GAReg ? Rt : Rs;

  // Can't fold if the register has more than one use.
  if (!MRI->hasOneUse(Reg))
    return false;
  // This can point to an ADDI or a LUI:
  MachineInstr &OffsetTail = *MRI->getVRegDef(Reg);
  if (OffsetTail.getOpcode() == RISCV::ADDI) {
    // The offset value has non zero bits in both %hi and %lo parts.
    // Detect an ADDI that feeds from a LUI instruction.
    MachineOperand &AddiImmOp = OffsetTail.getOperand(2);
    if (AddiImmOp.getTargetFlags() != RISCVII::MO_None)
      return false;
    int64_t OffLo = AddiImmOp.getImm();
    MachineInstr &OffsetLui =
        *MRI->getVRegDef(OffsetTail.getOperand(1).getReg());
    MachineOperand &LuiImmOp = OffsetLui.getOperand(1);
    if (OffsetLui.getOpcode() != RISCV::LUI ||
        LuiImmOp.getTargetFlags() != RISCVII::MO_None ||
        !MRI->hasOneUse(OffsetLui.getOperand(0).getReg()))
      return false;
    int64_t OffHi = OffsetLui.getOperand(1).getImm();
    Offset = (OffHi << 12) + OffLo;
    LLVM_DEBUG(dbgs() << "  Offset Instrs: " << OffsetTail
                      << "                 " << OffsetLui);
    DeadInstrs.insert(&OffsetTail);
    DeadInstrs.insert(&OffsetLui);
    return true;
  } else if (OffsetTail.getOpcode() == RISCV::LUI) {
    // The offset value has all zero bits in the lower 12 bits. Only LUI
    // exists.
    LLVM_DEBUG(dbgs() << "  Offset Instr: " << OffsetTail);
    Offset = OffsetTail.getOperand(1).getImm() << 12;
    DeadInstrs.insert(&OffsetTail);
    return true;
  }
  return false;
}

bool RISCVMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &HiLUI,
                                                  MachineInstr &LoADDI) {
  unsigned DestReg = LoADDI.getOperand(0).getReg();
  assert(MRI->hasOneUse(DestReg) && "expected one use for LoADDI");
  // LoADDI has only one use.
  MachineInstr &Tail = *MRI->use_begin(DestReg)->getParent();
  switch (Tail.getOpcode()) {
  default:
    LLVM_DEBUG(dbgs() << "Don't know how to get offset from this instr:"
                      << Tail);
    return false;
  case RISCV::ADDI: {
    // Offset is simply an immediate operand.
    int64_t Offset = Tail.getOperand(2).getImm();
    LLVM_DEBUG(dbgs() << "  Offset Instr: " << Tail);
    foldOffset(HiLUI, LoADDI, Tail, Offset);
    return true;
  } break;
  case RISCV::ADD: {
    // The offset is too large to fit in the immediate field of ADDI.
    // This can be in two forms:
    // 1) LUI hi_Offset followed by:
    //    ADDI lo_offset
    //    This happens in case the offset has non zero bits in
    //    both hi 20 and lo 12 bits.
    // 2) LUI (offset20)
    //    This happens in case the lower 12 bits of the offset are zeros.
    int64_t Offset;
    if (!matchLargeOffset(Tail, DestReg, Offset))
      return false;
    foldOffset(HiLUI, LoADDI, Tail, Offset);
    return true;
  } break;
  case RISCV::LB:
  case RISCV::LH:
  case RISCV::LW:
  case RISCV::LBU:
  case RISCV::LHU:
  case RISCV::LWU:
  case RISCV::LD:
  case RISCV::FLW:
  case RISCV::FLD:
  case RISCV::SB:
  case RISCV::SH:
  case RISCV::SW:
  case RISCV::SD:
  case RISCV::FSW:
  case RISCV::FSD: {
    // Transforms the sequence:            Into:
    // HiLUI:  lui vreg1, %hi(foo)          --->  lui vreg1, %hi(foo+8)
    // LoADDI: addi vreg2, vreg1, %lo(foo)  --->  lw vreg3, lo(foo+8)(vreg1)
    // Tail:   lw vreg3, 8(vreg2)
    if (Tail.getOperand(1).isFI())
      return false;
    // Register defined by LoADDI should be used in the base part of the
    // load\store instruction. Otherwise, no folding possible.
    unsigned BaseAddrReg = Tail.getOperand(1).getReg();
    if (DestReg != BaseAddrReg)
      return false;
    MachineOperand &TailImmOp = Tail.getOperand(2);
    int64_t Offset = TailImmOp.getImm();
    // Update the offsets in global address lowering.
    HiLUI.getOperand(1).setOffset(Offset);
    // Update the immediate in the Tail instruction to add the offset.
    Tail.RemoveOperand(2);
    MachineOperand &ImmOp = LoADDI.getOperand(2);
    ImmOp.setOffset(Offset);
    Tail.addOperand(ImmOp);
    // Update the base reg in the Tail instruction to feed from LUI.
    // Output of HiLUI is only used in LoADDI, no need to use
    // MRI->replaceRegWith().
    Tail.getOperand(1).setReg(HiLUI.getOperand(0).getReg());
    DeadInstrs.insert(&LoADDI);
    return true;
  } break;
  }
  return false;
}

bool RISCVMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) {
  if (skipFunction(Fn.getFunction()))
    return false;

  DeadInstrs.clear();
  MRI = &Fn.getRegInfo();
  for (MachineBasicBlock &MBB : Fn) {
    LLVM_DEBUG(dbgs() << "MBB: " << MBB.getName() << "\n");
    for (MachineInstr &HiLUI : MBB) {
      MachineInstr *LoADDI = nullptr;
      if (!detectLuiAddiGlobal(HiLUI, LoADDI))
        continue;
      LLVM_DEBUG(dbgs() << "  Found lowered global address with one use: "
                        << *LoADDI->getOperand(2).getGlobal() << "\n");
      // If the use count is only one, merge the offset
      detectAndFoldOffset(HiLUI, *LoADDI);
    }
  }
  // Delete dead instructions.
  for (auto *MI : DeadInstrs)
    MI->eraseFromParent();
  return true;
}

/// Returns an instance of the Merge Base Offset Optimization pass.
FunctionPass *llvm::createRISCVMergeBaseOffsetOptPass() {
  return new RISCVMergeBaseOffsetOpt();
}
<|endoftext|>