Esempio n. 1
0
void predict(Options::PredictEnum mode, ReportWriter *writer, const RomAccessor &rom, Trace &trace, const AnnotationResolver &annotations) {

	if (mode == Options::PRD_NEVER)
		return;

	bool limit_to_functions = mode == Options::PRD_FUNCTIONS;

	Profile profile("Predict", true);

	struct PredictBranch {
		const Annotation *annotation;
		Pointer from_pc;
		Pointer pc;
		uint16_t DP, P;
		uint8_t DB;
	};

	std::vector<PredictBranch> predict_brances;
	LargeBitfield has_op(256*64*1024);
	LargeBitfield inside_op(256 * 64 * 1024);

	for (auto opsit : trace.ops) {
		const Pointer pc = opsit.first;

		const Trace::OpVariantLookup &vl = opsit.second;
		const OpInfo &example = trace.variant(vl, 0);

		const uint8_t* data = rom.evalPtr(pc);
		const uint8_t opcode = data[0];

		uint32_t op_size = instruction_size(opcode, is_memory_accumulator_wide(example.P), is_index_wide(example.P));

		for (uint32_t i = 0; i < op_size; ++i) {
			has_op.set_bit(bank_add(pc, i));
			if (i!=0) inside_op.set_bit(bank_add(pc, i));
		}

		StringBuilder sb;

		Pointer target_jump, target_no_jump;
		bool branch_or_jump = decode_static_jump(opcode, rom, pc, &target_jump, &target_no_jump);

		const Hint *hint = annotations.hint(pc);
		if (hint && hint->has_hint(Hint::BRANCH_ALWAYS)) {
			target_no_jump = INVALID_POINTER;
		}
		if (hint && hint->has_hint(Hint::BRANCH_NEVER)) {
			target_jump = INVALID_POINTER;
		}

		if (!branch_or_jump)
			continue;

		const Annotation *source_annotation = nullptr, *target_annotation = nullptr;
		annotations.resolve_annotation(pc,          &source_annotation);

		if (target_jump != INVALID_POINTER) {
			trace.labels.set_bit(target_jump);
			annotations.resolve_annotation(target_jump, &target_annotation);
		}

		if (source_annotation || !limit_to_functions) {
			PredictBranch p;
			p.annotation = source_annotation;
			p.from_pc = pc;
			p.DB = example.DB;
			p.DP = example.DP;
			p.P  = example.P;
			if (target_jump != INVALID_POINTER && (target_annotation == source_annotation || !limit_to_functions)) {
				p.pc = target_jump;
				CUSTOM_ASSERT(target_jump != INVALID_POINTER);
				predict_brances.push_back(p);
			}
			if (target_no_jump != INVALID_POINTER && (!limit_to_functions || (target_no_jump >= source_annotation->startOfRange && target_no_jump <= source_annotation->endOfRange))) {
				// BRA,BRL and the jumps always branches/jumps
				p.pc = target_no_jump;
				CUSTOM_ASSERT(target_no_jump != INVALID_POINTER);
				predict_brances.push_back(p);
			}
		}
	}

	StringBuilder sb;
	sb.clear();

	if (writer)
		writer->writeSeperator("Prediction diagnostics");

	for (int pbi=0; pbi<(int)predict_brances.size(); ++pbi) {
		const PredictBranch pb = predict_brances[pbi];

		Pointer pc = pb.pc;
		Pointer r0 = limit_to_functions ? pb.annotation->startOfRange : 0, r1 = limit_to_functions ? pb.annotation->endOfRange : 0xFFFFFF;
		uint16_t P = pb.P, DP = pb.DP;
		uint8_t DB = pb.DB;

		bool P_unknown = false;

		if (inside_op[pc]) {
			if (writer) {
				sb.clear();
				sb.format("Predicted jump at %06X jumped inside instruction at %06X. Consider adding a \"hint branch_always/branch_never %06X\" annotation.", pc, pb.from_pc, pb.from_pc);
				writer->writeComment(sb);
			}
			printf("Warning; predicted jump went inside instruction at %06X (from %06X)\n", pc, pb.from_pc);
		}

		while (!has_op[pc] && pc >= r0 && pc <= r1) {

			// Make sure we don't run into a data scope
			const Annotation *data_scope = nullptr, *function_scope = nullptr;
			annotations.resolve_annotation(pc, &function_scope, &data_scope);
			if (data_scope)
				continue;
			if (function_scope && function_scope != pb.annotation)
				continue;

			uint8_t opcode = rom.evalByte(pc);

			bool abort_unknown_P = P_unknown;

			if (P_unknown) {
				switch(opcode) {
				case 0x02: // COP const
				case 0x40: // RTI
				case 0x6B: // RTL
				case 0x60: // RTS
				case 0x3B: // TSC
				case 0xBA: // TSX
				case 0x8A: // TXA
				case 0x9A: // TXS
				case 0x9B: // TXY
				case 0x98: // TYA
				case 0xBB: // TYX
				case 0xCB: // WAI
				case 0xEB: // XBA
				case 0xFB: // XCE
				case 0xAA: // TAX
				case 0xA8: // TAY
				case 0x5B: // TCD
				case 0x1B: // TCS
				case 0x7B: // TDC
				case 0xDB: // STP
				case 0x38: // SEC
				case 0xF8: // SED
				case 0x78: // SEI
				case 0xE2: // SEP
				case 0xC2: // REP
				case 0x18: // CLC
				case 0xD8: // CLD
				case 0x58: // CLI
				case 0xB8: // CLV
				case 0xCA: // DEX
				case 0x88: // DEY
				case 0xE8: // INX
				case 0xC8: // INY
				case 0xEA: // NOP
				case 0x8B: // PHB
				case 0x0B: // PHD
				case 0x4B: // PHK
				case 0x08: // PHP
				case 0xDA: // PHX
				case 0x5A: // PHY
				case 0x68: // PLA
				case 0xAB: // PLB
				case 0x2B: // PLD
				case 0x28: // PLP
				case 0xFA: // PLX
				case 0x7A: // PLY
					abort_unknown_P = false;
				}
			}

			if (abort_unknown_P) {
				if (writer) {
					sb.clear();
					sb.format("Aborting trace at %06X due to unknown processor status", pc);
					if (pb.annotation)
						sb.format("(in %s)", pb.annotation->name.c_str());
					writer->writeComment(sb);
				}
				break;
			}

			uint8_t operand = rom.evalByte(pc + 1);

			Pointer target_jump, target_no_jump;
			bool is_jump_or_branch = decode_static_jump(opcode, rom, pc, &target_jump, &target_no_jump);

			{
				Trace::OpVariantLookup l;
				l.count = 1;
				l.offset = (uint32_t)trace.ops_variants.size();
				trace.ops[pc] = l;
				OpInfo info;
				info.P = P_unknown ? 0 : P;
				info.DB = DB;
				info.DP = DP;
				info.X = info.Y = 0;
				info.jump_target = target_jump;
				info.indirect_base_pointer = INVALID_POINTER; // TODO: We should be able to set this one sometimes
				trace.ops_variants.push_back(info);

				// Note that DB and DP here represent a lie :)
			}

			const int op_size = instruction_size(rom.evalByte(pc), is_memory_accumulator_wide(P), is_index_wide(P));

			for (int i=0; i<op_size; ++i) {
				// TODO: We should do overlap test for entire range we are "using" now
				//       Also first might not always be best!
				trace.is_predicted.set_bit(bank_add(pc, i));
				has_op.set_bit(bank_add(pc, i));
				if (i != 0) inside_op.set_bit(bank_add(pc, i));
			}

			const Hint *hint = annotations.hint(pc);
			if (hint && hint->has_hint(Hint::BRANCH_NEVER)) {
				target_jump = INVALID_POINTER;
			}

			bool is_jsr = opcode == 0x20||opcode==0x22||opcode==0xFC;

			if (hint && hint->has_hint(Hint::JUMP_IS_JSR)) {
				 is_jump_or_branch = false;
				 is_jsr = true;
			}

			if (is_jump_or_branch) {
				const Annotation *function = nullptr;
				if (target_jump != INVALID_POINTER) {
					annotations.resolve_annotation(target_jump, &function);
					trace.labels.set_bit(target_jump);
				}

				if (limit_to_functions && writer && (!function || function != pb.annotation)) {
					sb.clear();
					sb.format("Branch going out of %s to ", pb.annotation->name.c_str());
					if (function) {
						sb.format("%s [%06X]", function->name.c_str(), target_jump);
					} else {
						sb.format("%06X", target_jump);
					}
					sb.format(". Not following due to range restriction.");
					writer->writeComment(sb);
				}
				
				if (target_jump != INVALID_POINTER) {
					PredictBranch npb = pb;
					npb.from_pc = pc;
					npb.pc = target_jump;
					predict_brances.push_back(npb);
				}

				if (hint && hint->has_hint(Hint::BRANCH_ALWAYS)) {
					// Never continoue after this op since it always diverges control flow
					continue;
				}

			} else if (opcode == 0xE2) {
				//	TODO:
				//	* When we get a REP or SEP parts or P become known again. We could track unknown per the three flags and update.
				//	* Updating DB/DP might be interesting
				if (operand & 0x10) P |= 0x0010;
				if (operand & 0x20) P |= 0x0020;
			} else if (opcode == 0xC2) {
				if (operand & 0x10) P &= ~0x0010;
				if (operand & 0x20) P &= ~0x0020;
			} else if (opcode == 0x28 || opcode == 0xFB) {
				P_unknown = true; // PLP or XCE
				// A jump or BRA, stop execution flow (not JSR or non-BRA-branch)
			} else if (opcode == 0x4C||opcode==0x5C||opcode==0x6C||opcode==0x7C||opcode==0x80) {
				if (writer && opcode != 0x80) {
					sb.clear();
					sb.format("Not following jump (opcode %02X) at %06X in %s. Only absolute jumps supported.", opcode, pc, pb.annotation->name.c_str());
					writer->writeComment(sb);
				}
				// TODO: if there is a trace annotation about jmp being jsr we could go on
				continue;
			} else if (is_jsr) {
				if (writer) {
					sb.clear();
					sb.format("Not following jump with subroutine (opcode %02X) at %06X", opcode, pc);
					if (pb.annotation)
						sb.format(" in %s.", pb.annotation->name.c_str());
					writer->writeComment(sb);
				}
			} else if (opcode == 0x40 || opcode == 0x6B || opcode == 0x60) {
				// some sort of return, stop execution flow
				continue;
			}
			pc += op_size;
		}
	}
}
Esempio n. 2
0
void Atlas::parameterizeCharts()
{
    ParameterizationQuality globalParameterizationQuality;

    // Paramterize the charts.
    uint diskCount = 0;
    const uint chartCount = m_chartArray.count();
    for (uint i = 0; i < chartCount; i++)\
    {
        Chart * chart = m_chartArray[i];

        bool isValid = false;

        if (chart->isDisk())
        {
            diskCount++;

            ParameterizationQuality chartParameterizationQuality;

            if (chart->faceCount() == 1) {
                computeSingleFaceMap(chart->unifiedMesh());

                chartParameterizationQuality = ParameterizationQuality(chart->unifiedMesh());
            }
            else {
                computeOrthogonalProjectionMap(chart->unifiedMesh());
                ParameterizationQuality orthogonalQuality(chart->unifiedMesh());

                computeLeastSquaresConformalMap(chart->unifiedMesh());
                ParameterizationQuality lscmQuality(chart->unifiedMesh());
                
                // If the orthogonal projection produces better results, just use that. @@ Make sure the orthogonal map is valid has no overlaps.
                /*if (orthogonalQuality.rmsStretchMetric() < lscmQuality.rmsStretchMetric()) {
                    computeOrthogonalProjectionMap(chart->unifiedMesh());
                    chartParameterizationQuality = orthogonalQuality;
                }
                else*/ {
                    chartParameterizationQuality = lscmQuality;
                }

                // If conformal map failed, 

                // @@ Experiment with other parameterization methods.
                //computeCircularBoundaryMap(chart->unifiedMesh());
                //computeConformalMap(chart->unifiedMesh());
                //computeNaturalConformalMap(chart->unifiedMesh());
                //computeGuidanceGradientMap(chart->unifiedMesh());
            }

            //ParameterizationQuality chartParameterizationQuality(chart->unifiedMesh());

            isValid = chartParameterizationQuality.isValid();

            if (!isValid)
            {
                nvDebug("*** Invalid parameterization.\n");
#if 0
                // Dump mesh to inspect problem:
                static int pieceCount = 0;
            
                StringBuilder fileName;
                fileName.format("invalid_chart_%d.obj", pieceCount++);
                exportMesh(chart->unifiedMesh(), fileName.str()); 
#endif
            }

            // @@ Check that parameterization quality is above a certain threshold.

            // @@ Detect boundary self-intersections.

            globalParameterizationQuality += chartParameterizationQuality;
        }

        if (!isValid)
        {
            //nvDebugBreak();
            // @@ Run the builder again, but only on this chart.
            //AtlasBuilder builder(chart->chartMesh());
        }

        // Transfer parameterization from unified mesh to chart mesh.
        chart->transferParameterization();

    }

    nvDebug("  Parameterized %d/%d charts.\n", diskCount, chartCount);
    nvDebug("  RMS stretch metric: %f\n", globalParameterizationQuality.rmsStretchMetric());
    nvDebug("  MAX stretch metric: %f\n", globalParameterizationQuality.maxStretchMetric());
    nvDebug("  RMS conformal metric: %f\n", globalParameterizationQuality.rmsConformalMetric());
    nvDebug("  RMS authalic metric: %f\n", globalParameterizationQuality.maxAuthalicMetric());
}
Esempio n. 3
0
void Chart::build(const HalfEdge::Mesh * originalMesh, const Array<uint> & faceArray)
{
    // Copy face indices.
    m_faceArray = faceArray;

    const uint meshVertexCount = originalMesh->vertexCount();

    m_chartMesh = new HalfEdge::Mesh();
    m_unifiedMesh = new HalfEdge::Mesh();

    Array<uint> chartMeshIndices;
    chartMeshIndices.resize(meshVertexCount, ~0);

    Array<uint> unifiedMeshIndices;
    unifiedMeshIndices.resize(meshVertexCount, ~0);

    // Add vertices.
    const uint faceCount = faceArray.count();
    for (uint f = 0; f < faceCount; f++)
    {
        const HalfEdge::Face * face = originalMesh->faceAt(faceArray[f]);
        nvDebugCheck(face != NULL);

        for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance())
        {
            const HalfEdge::Vertex * vertex = it.current()->vertex;
            const HalfEdge::Vertex * unifiedVertex = vertex->firstColocal();

            if (unifiedMeshIndices[unifiedVertex->id] == ~0)
            {
                unifiedMeshIndices[unifiedVertex->id] = m_unifiedMesh->vertexCount();

                nvDebugCheck(vertex->pos == unifiedVertex->pos);
                m_unifiedMesh->addVertex(vertex->pos);
            }

            if (chartMeshIndices[vertex->id] == ~0)
            {
                chartMeshIndices[vertex->id] = m_chartMesh->vertexCount();
                m_chartToOriginalMap.append(vertex->id);
                m_chartToUnifiedMap.append(unifiedMeshIndices[unifiedVertex->id]);

                HalfEdge::Vertex * v = m_chartMesh->addVertex(vertex->pos);
                v->nor = vertex->nor;
                v->tex = vertex->tex;
            }
        }
    }

    // This is ignoring the canonical map:
    // - Is it really necessary to link colocals?

    m_chartMesh->linkColocals();    
    //m_unifiedMesh->linkColocals();  // Not strictly necessary, no colocals in the unified mesh. # Wrong.

    // This check is not valid anymore, if the original mesh vertices were linked with a canonical map, then it might have
    // some colocal vertices that were unlinked. So, the unified mesh might have some duplicate vertices, because firstColocal()
    // is not guaranteed to return the same vertex for two colocal vertices.
    //nvCheck(m_chartMesh->colocalVertexCount() == m_unifiedMesh->vertexCount());

    // Is that OK? What happens in meshes were that happens? Does anything break? Apparently not...
    


    Array<uint> faceIndices(7);

    // Add faces.
    for (uint f = 0; f < faceCount; f++)
    {
        const HalfEdge::Face * face = originalMesh->faceAt(faceArray[f]);
        nvDebugCheck(face != NULL);

        faceIndices.clear();

        for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance())
        {
            const HalfEdge::Vertex * vertex = it.current()->vertex;
            nvDebugCheck(vertex != NULL);

            faceIndices.append(chartMeshIndices[vertex->id]);
        }

        m_chartMesh->addFace(faceIndices);

        faceIndices.clear();

        for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance())
        {
            const HalfEdge::Vertex * vertex = it.current()->vertex;
            nvDebugCheck(vertex != NULL);

            vertex = vertex->firstColocal();

            faceIndices.append(unifiedMeshIndices[vertex->id]);
        }

        m_unifiedMesh->addFace(faceIndices);
    }

    m_chartMesh->linkBoundary();
    m_unifiedMesh->linkBoundary();

    //exportMesh(m_unifiedMesh.ptr(), "debug_input.obj");

    if (m_unifiedMesh->splitBoundaryEdges()) {
        m_unifiedMesh = unifyVertices(m_unifiedMesh.ptr());
    }

    //exportMesh(m_unifiedMesh.ptr(), "debug_split.obj");

    // Closing the holes is not always the best solution and does not fix all the problems.
    // We need to do some analysis of the holes and the genus to:
    // - Find cuts that reduce genus.
    // - Find cuts to connect holes.
    // - Use minimal spanning trees or seamster.
    if (!closeHoles()) {
        /*static int pieceCount = 0;
        StringBuilder fileName;
        fileName.format("debug_hole_%d.obj", pieceCount++);
        exportMesh(m_unifiedMesh.ptr(), fileName.str());*/
    }

    m_unifiedMesh = triangulate(m_unifiedMesh.ptr());
    
    //exportMesh(m_unifiedMesh.ptr(), "debug_triangulated.obj");


    // Analyze chart topology.
    MeshTopology topology(m_unifiedMesh.ptr());
    m_isDisk = topology.isDisk();

    // This is sometimes failing, when triangulate fails to add a triangle, it generates a hole in the mesh.
    //nvDebugCheck(m_isDisk);

    /*if (!m_isDisk) {
        static int pieceCount = 0;
        StringBuilder fileName;
        fileName.format("debug_hole_%d.obj", pieceCount++);
        exportMesh(m_unifiedMesh.ptr(), fileName.str());
    }*/


#if 0
    if (!m_isDisk) {
        nvDebugBreak();

        static int pieceCount = 0;
        
        StringBuilder fileName;
        fileName.format("debug_nodisk_%d.obj", pieceCount++);
        exportMesh(m_chartMesh.ptr(), fileName.str()); 
    }
#endif

}
void SimPersistSet::write( Stream& stream, U32 tabStop, U32 flags )
{
   if( ( flags & SelectedOnly ) && !isSelected() )
      return;
      
   // If the selection is transient, we cannot really save it.
   // Just invoke the default SimObject::write and return.
      
   if( !getCanSave() )
   {
      Con::errorf( "SimPersistSet::write - transient set being saved: %d:%s (%s)",
         getId(), getClassName(), getName() );
      Parent::write( stream, tabStop, flags );
      return;
   }
   
   // If there are unresolved PIDs, give resolving them one last
   // chance before writing out the set.
   
   if( !mUnresolvedPIDs.empty() )
      resolvePIDs();
   
   // Write the set out.

   stream.writeTabs( tabStop );
   
   StringBuilder buffer;
   buffer.format( "new %s(%s", getClassName(), getName() ? getName() : "" );
   
   // Write the persistent IDs of all child objects into the set's
   // object constructor so we see them passed back to us through
   // processArguments when the object gets read in.
   
   const U32 numChildren = size();
   for( U32 i = 0; i < numChildren; ++ i )
   {
      SimObject* child = at( i );
      
      SimPersistID* pid = child->getPersistentId();
      AssertWarn( pid != NULL, "SimPersistSet::write - object without pid in persistent selection!" );
      if( !pid )
         continue;
         
      buffer.append( ',' );
      buffer.append( '"' );
      buffer.append( pid->getUUID().toString() );
      buffer.append( '"' );
   }
   
   buffer.append( ") {\r\n" );

   stream.write( buffer.length(), buffer.data() );
   
   // Write our object fields.
   
   writeFields( stream, tabStop + 1 );
   
   // Close our object definition.

   stream.writeTabs( tabStop );
   stream.write( 4, "};\r\n" );
}