233 Commits

Author SHA1 Message Date
Duncan P. N. Exon Smith
4e09f9bf86 ADT: Give ilist<T>::reverse_iterator a handle to the current node
Reverse iterators to doubly-linked lists can be simpler (and cheaper)
than std::reverse_iterator.  Make it so.

In particular, change ilist<T>::reverse_iterator so that it is *never*
invalidated unless the node it references is deleted.  This matches the
guarantees of ilist<T>::iterator.

(Note: MachineBasicBlock::iterator is *not* an ilist iterator, but a
MachineInstrBundleIterator<MachineInstr>.  This commit does not change
MachineBasicBlock::reverse_iterator, but it does update
MachineBasicBlock::reverse_instr_iterator.  See note at end of commit
message for details on bundle iterators.)

Given the list (with the Sentinel showing twice for simplicity):

     [Sentinel] <-> A <-> B <-> [Sentinel]

the following is now true:
 1. begin() represents A.
 2. begin() holds the pointer for A.
 3. end() represents [Sentinel].
 4. end() holds the poitner for [Sentinel].
 5. rbegin() represents B.
 6. rbegin() holds the pointer for B.
 7. rend() represents [Sentinel].
 8. rend() holds the pointer for [Sentinel].

The changes are #6 and #8.  Here are some properties from the old
scheme (which used std::reverse_iterator):
- rbegin() held the pointer for [Sentinel] and rend() held the pointer
  for A;
- operator*() cost two dereferences instead of one;
- converting from a valid iterator to its valid reverse_iterator
  involved a confusing increment; and
- "RI++->erase()" left RI invalid.  The unintuitive replacement was
  "RI->erase(), RE = end()".

With vector-like data structures these properties are hard to avoid
(since past-the-beginning is not a valid pointer), and don't impose a
real cost (since there's still only one dereference, and all iterators
are invalidated on erase).  But with lists, this was a poor design.

Specifically, the following code (which obviously works with normal
iterators) now works with ilist::reverse_iterator as well:

    for (auto RI = L.rbegin(), RE = L.rend(); RI != RE;)
      fooThatMightRemoveArgFromList(*RI++);

Converting between iterator and reverse_iterator for the same node uses
the getReverse() function.

    reverse_iterator iterator::getReverse();
    iterator reverse_iterator::getReverse();

Why doesn't iterator <=> reverse_iterator conversion use constructors?

In order to catch and update old code, reverse_iterator does not even
have an explicit conversion from iterator.  It wouldn't be safe because
there would be no reasonable way to catch all the bugs from the changed
semantic (see the changes at call sites that are part of this patch).

Old code used this API:

    std::reverse_iterator::reverse_iterator(iterator);
    iterator std::reverse_iterator::base();

Here's how to update from old code to new (that incorporates the
semantic change), assuming I is an ilist<>::iterator and RI is an
ilist<>::reverse_iterator:

            [Old]         ==>          [New]
    reverse_iterator(I)       (--I).getReverse()
    reverse_iterator(I)         ++I.getReverse()
  --reverse_iterator(I)           I.getReverse()
    reverse_iterator(++I)         I.getReverse()
          RI.base()          (--RI).getReverse()
          RI.base()            ++RI.getReverse()
        --RI.base()              RI.getReverse()
      (++RI).base()              RI.getReverse()
  delete &*RI, RE = end()         delete &*RI++
  RI->erase(), RE = end()         RI++->erase()

=======================================
Note: bundle iterators are out of scope
=======================================

MachineBasicBlock::iterator, also known as
MachineInstrBundleIterator<MachineInstr>, is a wrapper to represent
MachineInstr bundles.  The idea is that each operator++ takes you to the
beginning of the next bundle.  Implementing a sane reverse iterator for
this is harder than ilist.  Here are the options:
- Use std::reverse_iterator<MBB::i>.  Store a handle to the beginning of
  the next bundle.  A call to operator*() runs a loop (usually
  operator--() will be called 1 time, for unbundled instructions).
  Increment/decrement just works.  This is the status quo.
- Store a handle to the final node in the bundle.  A call to operator*()
  still runs a loop, but it iterates one time fewer (usually
  operator--() will be called 0 times, for unbundled instructions).
  Increment/decrement just works.
- Make the ilist_sentinel<MachineInstr> *always* store that it's the
  sentinel (instead of just in asserts mode).  Then the bundle iterator
  can sniff the sentinel bit in operator++().

I initially tried implementing the end() option as part of this commit,
but updating iterator/reverse_iterator conversion call sites was
error-prone.  I have a WIP series of patches that implements the final
option.

llvm-svn: 280032
2016-08-30 00:13:12 +00:00
Quentin Colombet
5f3ddf5325 [MFProperties] Introduce a FailedISel property.
This is used to communicate that the instruction selection pipeline
failed at some point.
Another way to achieve that would be to have some kind of conditional
scheduling in the PassManager, such that we only schedule a pass based
on the success/failure of another one. The property approach has the
advantage of being lightweight and solve the problem at stake.

llvm-svn: 279885
2016-08-26 23:49:01 +00:00
Quentin Colombet
947cfac2b5 [MachineFunction] Introduce a reset method.
This method allows to reset the state of a MachineFunction as if it was
just created. This will be used during the bring-up of GlobalISel to
provide a way to fallback on SelectionDAG. That way, we can start doing
correctness testing even if we are not able to select all functions via
the global instruction selector.

llvm-svn: 279876
2016-08-26 22:32:53 +00:00
Quentin Colombet
fcb250bf0c [MFProperties] Introduce a reset method with no argument.
This method allows to reset all the properties in one go.

llvm-svn: 279874
2016-08-26 22:09:11 +00:00
Quentin Colombet
e0a55b84fa [MFProperties][NFC] Rename clear into reset to match BitVector naming.
The name clear is used to reset all the bit in bitvectors and using it
to reset just properties was confusing.

llvm-svn: 279873
2016-08-26 22:09:08 +00:00
Matthias Braun
923da8d677 MachineFunctionProperties/MIRParser: Rename AllVRegsAllocated->NoVRegs, compute it
Rename AllVRegsAllocated to NoVRegs. This avoids the connotation of
running after register and simply describes that no vregs are used in
a machine function. With that we can simply compute the property and do
not need to dump/parse it in .mir files.

Differential Revision: http://reviews.llvm.org/D23850

llvm-svn: 279698
2016-08-25 01:27:13 +00:00
Matthias Braun
c10c0e4ef4 MachineFunction: Introduce NoPHIs property
I want to compute the SSA property of .mir files automatically in
upcoming patches. The problem with this is that some inputs will be
reported as static single assignment with some passes claiming not to
support SSA form.  In reality though those passes do not support PHI
instructions => Track the presence of PHI instructions separate from the
SSA property.

Differential Revision: https://reviews.llvm.org/D22719

llvm-svn: 279573
2016-08-23 21:19:49 +00:00
Tim Shen
33e4d80307 [GraphTraits] Replace all NodeType usage with NodeRef
This should finish the GraphTraits migration.

Differential Revision: http://reviews.llvm.org/D23730

llvm-svn: 279475
2016-08-22 21:09:30 +00:00
Duncan P. N. Exon Smith
68b6058de5 ADT: Remove ilist_*sentinel_traits, NFC
Remove all the dead code around ilist_*sentinel_traits.  This is a
follow-up to gutting them as part of r279314 (originally r278974),
staged to prevent broken builds in sub-projects.

Uses were removed from clang in r279457 and lld in r279458.

llvm-svn: 279473
2016-08-22 20:51:00 +00:00
Matthias Braun
670a716948 MachineFunction: Cleanup/simplify MachineFunctionProperties::print()
- Always compile print() regardless of LLVM_ENABLE_DUMP. (We usually
  only gard dump() functions with that).
- Only show the set properties to reduce output clutter.
- Remove the unused variant that even shows the unset properties.
- Fix comments

llvm-svn: 279338
2016-08-19 22:31:45 +00:00
Matthias Braun
57051db2dd MachineFunction: Make LastProperty an alias of the last property
This avoids unnecessary cases in switch statements covering all
properties.

llvm-svn: 279337
2016-08-19 22:31:42 +00:00
Tim Shen
823bde34b3 [GraphTraits] Make nodes_iterator dereference to NodeType*/NodeRef
Currently nodes_iterator may dereference to a NodeType* or a NodeType&. Make them all dereference to NodeType*, which is NodeRef later.

Differential Revision: https://reviews.llvm.org/D23704
Differential Revision: https://reviews.llvm.org/D23705

llvm-svn: 279326
2016-08-19 21:20:13 +00:00
Duncan P. N. Exon Smith
2e8f72520b ADT: Share code for embedded sentinel traits, NFC
Share code for the (mostly problematic) embedded sentinel traits.
- Move the LLVM_NO_SANITIZE("object-size") attribute to
  ilist_half_embedded_sentinel_traits and ilist_embedded_sentinel_traits
  (previously it spread throughout the code duplication).
- Add an ilist_full_embedded_sentinel_traits which has no UB (but has
  the downside of storing the complete node).
- Replace all the custom sentinel traits in LLVM with a declaration of
  ilist_sentinel_traits that inherits from one of the embedded sentinel
  traits classes.

There are still custom sentinel traits in other LLVM subprojects.  I'll
remove those in a follow-up.

Nothing at all should be changing here, this is just rearranging code.
Note that the final goal here is to remove the sentinel traits
altogether, settling on the memory layout of
ilist_half_embedded_sentinel_traits without the UB.  This intermediate
step moves the logic into ilist.h.

llvm-svn: 278513
2016-08-12 15:00:55 +00:00
Ahmed Bougacha
e7864c5269 [GlobalISel] Add Selected MachineFunction property.
Selected: the InstructionSelect pass ran and all pre-isel generic
instructions have been eliminated; i.e., all instructions are now
target-specific or non-pre-isel generic instructions (e.g., COPY).

Since only pre-isel generic instructions can have generic virtual register
operands, this also means that all generic virtual registers have been
constrained to virtual registers (assigned to register classes) and that
all sizes attached to them have been eliminated.

This lets us enforce certain invariants across passes.
This property is GlobalISel-specific, but is always available.

llvm-svn: 277482
2016-08-02 16:49:19 +00:00
Ahmed Bougacha
dec4756cb5 [GlobalISel] Add RegBankSelected MachineFunction property.
RegBankSelected: the RegBankSelect pass ran and all generic virtual
registers have been assigned to a register bank.

This lets us enforce certain invariants across passes.
This property is GlobalISel-specific, but is always available.

llvm-svn: 277475
2016-08-02 16:17:10 +00:00
Ahmed Bougacha
e15921e8b6 [GlobalISel] Add Legalized MachineFunction property.
Legalized: The MachineLegalizer ran; all pre-isel generic instructions
have been legalized, i.e., all instructions are now one of:
  - generic and always legal (e.g., COPY)
  - target-specific
  - legal pre-isel generic instructions.

This lets us enforce certain invariants across passes.
This property is GlobalISel-specific, but is always available.

llvm-svn: 277470
2016-08-02 15:10:25 +00:00
Matthias Braun
91722d430e MachineFunction: Return reference for getFrameInfo(); NFC
getFrameInfo() never returns nullptr so we should use a reference
instead of a pointer.

llvm-svn: 277017
2016-07-28 18:40:00 +00:00
Justin Lebar
700af803a3 [CodeGen] Take a MachineMemOperand::Flags in MachineFunction::getMachineMemOperand.
Summary:
Previously we took an unsigned.

Hooray for type-safety.

Reviewers: chandlerc

Subscribers: dsanders, llvm-commits

Differential Revision: http://reviews.llvm.org/D22282

llvm-svn: 275591
2016-07-15 18:26:59 +00:00
Benjamin Kramer
e80783f62f Pass DebugLoc and SDLoc by const ref.
This used to be free, copying and moving DebugLocs became expensive
after the metadata rewrite. Passing by reference eliminates a ton of
track/untrack operations. No functionality change intended.

llvm-svn: 272512
2016-06-12 15:39:02 +00:00
Matthias Braun
45444fa103 MachineFunction: Add a const modifier to print() parameter
llvm-svn: 268657
2016-05-05 18:14:43 +00:00
Derek Schuff
ba44d83fd8 Improve error message reporting for MachineFunctionProperties
When printing the properties required by a pass, only print the
properties that are set, and not those that are clear (only properties
that are set are verified, clear properties are "don't-care").

llvm-svn: 267070
2016-04-21 22:19:24 +00:00
Derek Schuff
bf676249bc Replace MachineRegisterInfo::TracksLiveness with a MachineFunctionProperty
Use the MachineFunctionProperty mechanism to indicate whether the
liveness info is accurate instead of a bool flag on MRI.
Keeps the MRI accessor function for convenience. NFC

Differential Revision: http://reviews.llvm.org/D18767

llvm-svn: 266020
2016-04-11 23:32:13 +00:00
Derek Schuff
008fe7e9da Replace MachineRegisterInfo::isSSA() with a MachineFunctionProperty
Use the MachineFunctionProperty mechanism to indicate whether a MachineFunction
is in SSA form instead of a custom method on MachineRegisterInfo. NFC

Differential Revision: http://reviews.llvm.org/D18574

llvm-svn: 265318
2016-04-04 18:03:29 +00:00
Derek Schuff
f9d46e7d23 Add a print method to MachineFunctionProperties for better error messages
This makes check failures much easier to understand.
Make it empty (but leave it in the class) for NDEBUG builds.

Differential Revision: http://reviews.llvm.org/D18529

llvm-svn: 264780
2016-03-29 20:28:20 +00:00
Derek Schuff
68842ff3b9 Move use of scoped enum in MachineFunctionProperties after its declaration
This should fix the build with MSVC 2013

llvm-svn: 264668
2016-03-28 21:38:35 +00:00
Derek Schuff
9f833b6097 Introduce MachineFunctionProperties and the AllVRegsAllocated property
MachineFunctionProperties represents a set of properties that a MachineFunction
can have at particular points in time. Existing examples of this idea are
MachineRegisterInfo::isSSA() and MachineRegisterInfo::tracksLiveness() which
will eventually be switched to use this mechanism.
This change introduces the AllVRegsAllocated property; i.e. the property that
all virtual registers have been allocated and there are no VReg operands
left.

With this mechanism, passes can declare that they require a particular property
to be set, or that they set or clear properties by implementing e.g.
MachineFunctionPass::getRequiredProperties(). The MachineFunctionPass base class
verifies that the requirements are met, and handles the setting and clearing
based on the delcarations. Passes can also directly query and update the current
properties of the MF if they want to have conditional behavior.

This change annotates the target-independent post-regalloc passes; future
changes will also annotate target-specific ones.

Reviewers: qcolombet, hfinkel

Differential Revision: http://reviews.llvm.org/D18421

llvm-svn: 264593
2016-03-28 17:05:30 +00:00
Richard Trieu
0068b48338 Fix uninitialized member bool. Detected by ASan.
llvm-svn: 262999
2016-03-09 06:31:25 +00:00
Manuel Klimek
44fcd6336a Fix problem with uninitilialized bool found by asan.
llvm-svn: 262934
2016-03-08 16:17:48 +00:00
Justin Bogner
6b5f8cc6e0 Annotate our undefined behaviour to sneak it past the sanitizers
We have known UB in some ilists where we static cast half nodes to
(larger) derived types and use the address. See llvm.org/PR26753.

This needs to be fixed, but in the meantime it'd be nice if running
ubsan didn't complain. This adds annotations in the two places where
ubsan complains while running check-all of a sanitized clang build.

llvm-svn: 262683
2016-03-04 01:52:47 +00:00
Matthias Braun
8136db87af MachineVerifier: Add parameter to choose if MachineFunction::verify() aborts
The abort on error behaviour is unpractical for debugger and unittest
usage.

llvm-svn: 260904
2016-02-15 19:25:31 +00:00
Quentin Colombet
44cd309151 [MachineFunction] Constify getter. NFC.
llvm-svn: 258207
2016-01-19 22:31:12 +00:00
Reid Kleckner
00daa6cd20 [WinEH] Move WinEHFuncInfo from MachineModuleInfo to MachineFunction
Summary:
Now that there is a one-to-one mapping from MachineFunction to
WinEHFuncInfo, we don't need to use a DenseMap to select the right
WinEHFuncInfo for the current funclet.

The main challenge here is that X86WinEHStatePass is an IR pass that
doesn't have access to the MachineFunction. I gave it its own
WinEHFuncInfo object that it uses to calculate state numbers, which it
then throws away. As long as nobody creates or removes EH pads between
this pass and SDAG construction, we will get the same state numbers.

The other thing X86WinEHStatePass does is to mark the EH registration
node. Instead of communicating which alloca was the registration through
WinEHFuncInfo, I added the llvm.x86.seh.ehregnode intrinsic.  This
intrinsic generates no code and simply marks the alloca in use.

Reviewers: JCTremoulet

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D14668

llvm-svn: 253378
2015-11-17 21:10:25 +00:00
Duncan P. N. Exon Smith
bc2ac24566 ADT: Avoid relying on UB in ilist_node::getNextNode()
Re-implement `ilist_node::getNextNode()` and `getPrevNode()` without
relying on the sentinel having a "next" pointer.  Instead, get access to
the owning list and compare against the `begin()` and `end()` iterators.

This only works when the node *can* get access to the owning list.  The
new support is in `ilist_node_with_parent<>`, and any class `Ty`
inheriting from `ilist_node<NodeTy>` that wants `getNextNode()` and/or
`getPrevNode()` should inherit from
`ilist_node_with_parent<NodeTy, ParentTy>` instead.  The requirements:

  - `NodeTy` must have a `getParent()` function that returns the parent.
  - `ParentTy` must have a `getSublistAccess()` static that, given a(n
    ignored) `NodeTy*` (to determine which list), returns a member field
    pointer to the appropriate `ilist<>`.

This isn't the cleanest way to get access to the owning list, but it
leverages the API already used in the IR hierarchy (see, e.g.,
`Instruction::getSublistAccess()`).

If anyone feels like ripping out the calls to `getNextNode()` and
`getPrevNode()` and replacing with direct iterator logic, they can also
remove the access function, etc., but as an incremental step, I'm
maintaining the API where it's currently used in tree.

If these requirements are *not* met, call sites with access to the ilist
can call `iplist<NodeTy>::getNextNode(NodeTy*)` directly, as in
ilistTest.cpp.

Why rewrite this?

The old code was broken, calling `getNext()` on a sentinel that possibly
didn't have a "next" pointer at all!  The new code avoids that
particular flavour of UB (see the commit message for r252538 for more
details about the "lucky" memory layout that made this function so
interesting).

There's still some UB here: the end iterator gets downcast to `NodeTy*`,
even when it's a sentinel (which is typically
`ilist_half_node<NodeTy*>`).  I'll tackle that in follow-up commits.
See this llvm-dev thread for more details:
http://lists.llvm.org/pipermail/llvm-dev/2015-October/091115.html

What's the danger?

There might be some code that relies on `getNextNode()` or
`getPrevNode()` *never* returning `nullptr` -- i.e., that relies on them
being broken when the sentinel is an `ilist_half_node<NodeTy>`.  I tried
to root out those cases with the audits I did leading up to r252380, but
it's possible I missed one or two.  I hope not.

(If (1) you have out-of-tree code, (2) you've reverted r252380
temporarily, and (3) you get some weird crashes with this commit, then I
recommend un-reverting r252380 and auditing the compile errors looking
for "strange" implicit conversions.)

llvm-svn: 252694
2015-11-11 02:26:42 +00:00
Duncan P. N. Exon Smith
0c724c41a0 CodeGen: Remove implicit iterator conversions from MBB.cpp
Remove implicit ilist iterator conversions from MachineBasicBlock.cpp.

I've also added an overload of `splice()` that takes a pointer, since
it's a natural API.  This is similar to the overloads I added for
`remove()` and `erase()` in r249867.

llvm-svn: 249883
2015-10-09 19:36:12 +00:00
Duncan P. N. Exon Smith
9ccd2df30d CodeGen: Remove implicit conversions from Analysis and BranchFolding
Remove a few more implicit ilist iterator conversions, this time from
Analysis.cpp and BranchFolding.cpp.

I added a few overloads for `remove()` and `erase()`, which quite
naturally take pointers as well as iterators as parameters.  This will
reduce the churn at least in the short term, but I don't really have a
problem with these existing for longer.

llvm-svn: 249867
2015-10-09 18:23:49 +00:00
NAKAMURA Takumi
1c96707452 Trailing whitespaces.
llvm-svn: 249286
2015-10-05 04:44:08 +00:00
NAKAMURA Takumi
0a7aa4f31c Prune trailing whitespaces in comment lines.
llvm-svn: 249285
2015-10-05 04:43:57 +00:00
NAKAMURA Takumi
421c86dd7c Reformat blank lines.
llvm-svn: 249284
2015-10-05 04:43:48 +00:00
David Majnemer
0f63c81aa5 [WinEH] Moved funclet pads should be in relative order
We shifted the MachineBasicBlocks to the end of the MachineFunction in
DFS order.  This will not ensure that MachineBasicBlocks which fell
through to one another will remain contiguous.  Instead, implement
a stable sort algorithm for iplist.

This partially reverts commit r214150.

llvm-svn: 247978
2015-09-18 08:18:07 +00:00
Alex Lorenz
7b1d22a17d PseudoSourceValue: Replace global manager with a manager in a machine function.
This commit removes the global manager variable which is responsible for
storing and allocating pseudo source values and instead it introduces a new
manager class named 'PseudoSourceValueManager'. Machine functions now own an
instance of the pseudo source value manager class.

This commit also modifies the 'get...' methods in the 'MachinePointerInfo'
class to construct pseudo source values using the instance of the pseudo
source value manager object from the machine function.

This commit updates calls to the 'get...' methods from the 'MachinePointerInfo'
class in a lot of different files because those calls now need to pass in a
reference to a machine function to those methods.

This change will make it easier to serialize pseudo source values as it will
enable me to transform the mips specific MipsCallEntry PseudoSourceValue
subclass into two target independent subclasses.

Reviewers: Akira Hatanaka
llvm-svn: 244693
2015-08-11 23:09:45 +00:00
Alex Lorenz
e961fcfaa4 MIR Serialization: Serialize the external symbol machine operands.
Reviewers: Duncan P. N. Exon Smith
llvm-svn: 242806
2015-07-21 16:59:53 +00:00
Mehdi Amini
19bede10ac Redirect DataLayout from TargetMachine to Module in MachineFunction
Summary:
This change is part of a series of commits dedicated to have a
single DataLayout during compilation by using always the one owned by the
module.

Reviewers: echristo

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D10984

From: Mehdi Amini <mehdi.amini@apple.com>
llvm-svn: 241610
2015-07-07 18:20:57 +00:00
Alexander Kornienko
f993659b8f Revert r240137 (Fixed/added namespace ending comments using clang-tidy. NFC)
Apparently, the style needs to be agreed upon first.

llvm-svn: 240390
2015-06-23 09:49:53 +00:00
Alexander Kornienko
40cb19d802 Fixed/added namespace ending comments using clang-tidy. NFC
The patch is generated using this command:

tools/clang/tools/extra/clang-tidy/tool/run-clang-tidy.py -fix \
  -checks=-*,llvm-namespace-comment -header-filter='llvm/.*|clang/.*' \
  llvm/lib/


Thanks to Eugene Kosov for the original patch!

llvm-svn: 240137
2015-06-19 15:57:42 +00:00
Aaron Ballman
0b45511a2e Removing LLVM_DELETED_FUNCTION, as MSVC 2012 was the last reason for requiring the macro. NFC; LLVM edition.
llvm-svn: 229340
2015-02-15 22:54:22 +00:00
Eric Christopher
2ab75347e5 Add a similar templated cast for getSubtarget off of the MachineFunction
to save typing a lot of static_casts.

llvm-svn: 227621
2015-01-30 22:02:19 +00:00
Mehdi Amini
c87fbe6ada Use a Factory Method for MachineFunctionInfo Creation
The goal is to allows MachineFunctionInfo to override this create
function to customize the creation.
No change intended in existing backend in this patch.

llvm-svn: 225292
2015-01-06 20:05:02 +00:00
Rafael Espindola
c98553226f Remove unused member variable.
Fixes pr20904.

llvm-svn: 219706
2014-10-14 18:53:16 +00:00
Eric Christopher
74f3138425 Have setSubtarget take a const subtarget.
llvm-svn: 218490
2014-09-26 01:28:13 +00:00
Eric Christopher
67c04e77e5 Have MachineFunction cache a pointer to the subtarget to make lookups
shorter/easier and have the DAG use that to do the same lookup. This
can be used in the future for TargetMachine based caching lookups from
the MachineFunction easily.

Update the MIPS subtarget switching machinery to update this pointer
at the same time it runs.

llvm-svn: 214838
2014-08-05 02:39:49 +00:00